Socket.BeginReceive合并TCP数据包

是否有一个设置我可以用来防止将多个TCP数据包合并到Socket.BeginReceive回调中的单个缓冲区?

你的下意识反应是我无法阻止TCP分裂/合并数据,但这不是我要求的 ; 我可以清楚地看到在Wireshark中收到的各个数据包,我唯一关心的是延迟,即一旦到达就处理该段。 这并不意味着我不知道如何处理拆分/合并段,但这意味着我想避免延迟。

我的代码如下所示:

 void WaitForData(ISocketInfo soc) { if (socket != null && socket.Connected) socket.BeginReceive(buffer, 0, buffer.Length, SocketFlags.None, OnPacketReceived, socket); } void OnPacketReceived(IAsyncResult asyn) { try { var socket = (ISocketInfo)asyn.AsyncState; numberOfBytesReceived = socket.EndReceive(asyn); if (numberOfBytesReceived > 0) { _queue.Enqueue(buffer, 0, numberOfBytesReceived); OnNewDataReceived(); } WaitForData(socketInfo); } catch (SocketException ex) { Log.Warn("Socket error while receiving packet", ex); Close(); } } 

当我在WireShark中检查这些数据包时,我可以看到每50ms接收一个TCP数据包,每个数据包(比如说)100个字节。 但有时在我的应用程序中有100毫秒的延迟, OnPacketReceived方法获得200个字节。

由于WireShark确认这不是操作系统/网络问题,这可能是什么问题? OnPacketReceived只是在后台线程上触发,因此它不会阻塞该方法,并且程序实际上并没有消耗太多CPU。

(更新)

好像我没有清楚地表达我的问题。 我的问题不是如何解析数据,如果它跨段分割。 我的协议定义得很好(即START_COOKIE,LENGTH,DATA,CRC),我收到它后立即将数据排入字节FIFO(上面代码片段内的_queue.Enqueue调用),这样我就可以很容易地异步解析它。

问题是,如果我看到数据包没有。 1(100字节)在Wireshark + 50ms,数据包号码。 Wireshark中的2(100字节)+ 100ms,我的应用程序没有阻止OnPacketReceived方法而且没有CPU,为什么.NET会每隔一段时间调用OnPacketReceived并将两个数据包合并为一个?

您可能会收到更少或更多,不能保证您将收到确切的发送内容。

来自MSDN:

无法保证您发送的数据会立即显示在网络上。 为了提高网络效率,底层系统可能会延迟传输,直到收集到大量的传出数据。 成功完成BeginSend方法意味着底层系统有足够的空间来缓冲网络发送的数据。

这是一篇很好的文章来解释这个以及如何解决您的问题。

根据定义, TCP是一个连续的数据流(它的主要特征是,似乎根本没有数据包,与UDP不同)。 因此,由您的软件来定义消息格式。 你可以:

  • 用每个消息的长度标记每条消息的开头(http就是这样)。
  • 在邮件末尾添加分隔标记(/ n)。 要让接收方知道何时开始和停止读取单个消息,请标记消息的开头和结尾(使用无法输入的特殊符号)。

总而言之,您必须考虑将流拆分为消息。 或者在TCP之上使用HTTP,WebSockets或其他消息格式实现。