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或其他消息格式实现。