Socket.BeginReceive合并TCP数据包

时间:2016-12-30 21:08:20

标签: c# .net sockets networking 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调用),这样我就可以轻松解析它是异步的。

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

2 个答案:

答案 0 :(得分:1)

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

来自MSDN:

  

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

Here是一篇很好的文章,可以解释这一点,以及如何解决问题。

答案 1 :(得分:0)

根据定义,

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

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

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