使用 TcpClient/TcpListener,接收未知数量的单独消息的最佳方法是什么?

Using TcpClient/TcpListener, what is the best method to receive an unknown amount of separate messages?

我当前的 ClientHandler 方法接收一个 TcpClient,在所述客户端上打开一个 NetStream,然后像这样开始从中提取数据:

                try
            {
                //byte[] bytes = new byte[client.ReceiveBufferSize]; 
                byte[] bytes = new byte[100];
                int bytes_read = netstream.Read(bytes, 0, bytes.Length);
                Console.WriteLine(DateTime.Now.ToString() + ": Bytes Read");

                while ( bytes_read > 0)
                {
                    memstream.Write(bytes, 0, bytes_read);
                    bytes_read = netstream.Read(bytes, 0, bytes.Length);

                    if (memstream.Length > 255)
                    {
                        MemStreamRead(memstream);
                        memstream.Flush();
                        OkResponse(netstream);
                    }
                }
            }

            catch (Exception e)
            {
                Console.WriteLine("No longer connected");
                connected = false;
                Console.WriteLine(e);
                Console.WriteLine(e.StackTrace);
            }  

我已经可以从客户端获取第一条消息,但我很难考虑如何为新消息保持流打开状态,并决定何时完成当前消息。

netstream.DataAvailable 似乎可以工作,我假设它检测到 netstream 不再获取更多数据,而 returns 错误。但是,一旦有新数据可用,我该如何重新开始收听呢?

在此先感谢您的帮助!

how do I start listening again once new data is available?

再次致电Read。这就是大多数循环的工作方式,只需调用非阻塞 Read(或专用线程上的阻塞 Read),然后在获取数据时对其进行处理。

when my current message is done

那是你遇到的另一个问题。当 OS 套接字缓冲区中没有更多数据时,您的消息不一定完成,当它按照您的规则定义完成时就完成了。您通常会使用消息长度打开消息,然后不断向 OS 请求数据,直到您填充了给定长度的缓冲区(或由于超时而失败)。

TcpClient 对象及其 netstream 保存在您可以反复使用的地方。然后根据需要经常调用 netstream.Read() (非阻塞)。 DataAvailable 有数据读取时会告诉你。

注意:传入的 TCP 数据被添加到一个大缓冲区中,不能像接收到的那样分离回数据包。
使用足够大的大小调用 Read() 将从缓冲区中获取 'all' 数据。
您将需要自己的更高级别的协议来分离它。易于实施...

另一方面,在 UDP 上,Read() 将 return 1 个数据包的数据。在 Read() 之后将 return 下一个数据包的数据。保留分组'structure'。

对于TCP高层协议,你可以这样做:

  • 以 1 个或多个 0xFF 字节开始每条消息(根据 您的 定义的消息,长度可能为 1 字节或 100.000 字节),因此您可以轻松检测到消息开始。
  • 以 0x02(STX,含义为 'Start of text')开始,以 0x03(ETX,文本结尾)结束,因此您可以排除 'nonsense' 数据(例如活动消息)周围没有STX、ETX。
  • 如果您要发送可能包含 0x02、0x03、0xFF 的二进制数据,则每条消息都可以以 nnnnnn 开头,nnnnnn 是一个数字,以字节为单位定义消息长度。废话数据不能在这里分开。

编辑:
另行通知:
如果连接被远端关闭,Read()仍然会执行成功,但不会执行return数据。您必须以另一种方式找出连接是否即将关闭。有关更多有用信息,请参阅我对其他 SO 网络问题的回答。