0

我必须将 2 个系统与 TCP 通信集成到一个项目中 - 开发了一个自定义协议。

通过协议发送的消息如下所示:

MsgLength - 10 positions (in chars, not bytes)
MessageType - 20 positions
Message Number - 9 positions
Timestamp - 19 positions
version - 2 positions
Body - variable (depending on first field)

我正在研究 Socket 编程,但我想知道等待完整 msg 到达的最佳方法是什么。可能我需要将字节转换为剩余的字符数。因为套接字 API 仅适用于字节。

欢迎所有提示、代码示例、博客文章 :-)

编辑:这是我的代码的第一个版本,我使用异步方法,因为多个客户端可以同时连接。

  private static void Listen()
        {
            while (true)
            {
                //TcpClient client = _listener.AcceptTcpClient();
                allDone.Reset();
                _listener.BeginAcceptTcpClient(new AsyncCallback(AcceptCallback), _listener);
                allDone.WaitOne();
            }
        }


public static void AcceptCallback(IAsyncResult ar)
        {
            // Signal the main thread to continue.
            allDone.Set();

            // Get the socket that handles the client request.
            //TcpListener listener = (TcpListener)ar.AsyncState;
            TcpClient handler = _listener.EndAcceptTcpClient(ar);

            // Create the state object.
            StateObject state = new StateObject();
            state.client = handler;

            handler.GetStream().BeginRead(state.buffer, 0, StateObject.BufferSize, new AsyncCallback(ReadCallback), state);

        }

public static void ReadCallback(IAsyncResult ar)
        {
            String content = String.Empty;
            Encoding enc = Encoding.GetEncoding("ISO-8859-1");

            // Retrieve the state object and the handler socket
            // from the asynchronous state object.
            StateObject state = (StateObject)ar.AsyncState;
            TcpClient handler = state.client;

               int bytesRead = handler.GetStream().EndRead(ar);
        state.TotalHeaderBytesRead += bytesRead;

            if (bytesRead == 60)
            {
                string header = enc.GetString(state.buffer);
                Header h = HeaderParser.Parse(header);
                //todo read the body

                byte[] bodyBuffer = new byte[((HeaderPlaPo)h).Length - 60];
                state.buffer = bodyBuffer;
                handler.GetStream().BeginRead(state.buffer, 0, bodyBuffer.Length, new AsyncCallback(ReadBodyCallback), state);
                Logger.Log(string.Format("received header {0}", header), System.Diagnostics.TraceLevel.Info);
            }
            else
            {
                // Not all data of header received. Get more.
                handler.GetStream().BeginRead(state.buffer, 0, StateObject.BufferSize - state.TotalHeaderBytesRead, new AsyncCallback(ReadHeaderCallback), state);
            }
        }



public static void ReadBodyCallback(IAsyncResult ar)
        {
            Encoding enc = Encoding.GetEncoding("ISO-8859-1");

            // Retrieve the state object and the handler socket
            // from the asynchronous state object.
            StateObject state = (StateObject)ar.AsyncState;
            TcpClient handler = state.client;

            int bytesRead = handler.GetStream().EndRead(ar);
 int bytesRead = handler.GetStream().EndRead(ar);
        state.TotalBodyBytesRead += bytesRead;

        if (state.buffer.Length == state.TotalBodyBytesRead)

{ //todo 我们收到所有字符串 body = enc.GetString(state.buffer); Logger.Log(string.Format("received body {0}", body), System.Diagnostics.TraceLevel.Info); } else { handler.GetStream().BeginRead(state.buffer, bytesRead, state.buffer.Length - state.TotalBodyBytesRead, ReadBodyCallback, state); } }

public class StateObject { // 客户端套接字。公共 TcpClient 客户端 = null; // 接收缓冲区的大小。公共静态 int BufferSize = 60; // 接收缓冲区。公共字节[]缓冲区=新字节[缓冲区大小];// 接收到的数据字符串 public StringBuilder sb = new StringBuilder(); //标头公共标头标头;公共机构;公共 Int32 TotalBodyBytesRead; 公共 Int32 TotalHeaderBytesRead; }

此代码适用于发送数据并关闭连接的客户端,但是当使用连接的客户端多次发送时,数据不会被读取->我应该在读取完整正文后关闭连接吗?

4

1 回答 1

1

你没有提到你使用什么语言,我给出了Java的例子,如果你需要其他语言的让我知道。无论如何,您需要一口气读取 60 (10+20+9+19+2) 个字节。将前 10 个字节(字符)转换为整数,然后将该字节数读入 byte[] 数组。

例如:

try
{
    byte[] buffer = new byte[60];
    mySocket.getInputStream().read(buffer, 0, 60);
    String header = buffer.toString();
    int length = Integer.parseInt(header.substring(0, 9));
    byte[] body = new byte[length];
    mySocket.getInputStream().read(body, 0, length);
}
catch (Exception e)
{
   e.printStackTrace();
}

编辑:更改为 C#

 byte[] buffer = new byte[60];
 mySocket.Receive(buffer, 60, SocketFlags.None);
 // You should ckeck this, UTF7 or UTF8 or UNICODE, etc
 string header = Encoding.UTF7.GetString(buffer, 0, 60);
 int length = Convert.ToInt32(header.Substring(0, 10));
 byte[] body = new byte[length];
 int offset=0;
 // Keep reading the socket until all bytes has been received
 while (length > 0) 
 {
     int ret=mySocket.Receive(body, offset, length, SocketFlags.None);
     if (ret > 0) 
     {
        offset += ret;
        length -= ret;
     }
     else 
        if (ret == 0) 
        {
            // peer has close the socket
        }
        else
        {
           // there is an error in the socket.
        }

  }
于 2013-07-01T21:53:59.040 回答