Warning: file_get_contents(/data/phpspider/zhask/data//catemap/3/sockets/2.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# 在C中使用套接字api时,TCP数据包是如何到达的#_C#_Sockets_Networking_Tcp - Fatal编程技术网

C# 在C中使用套接字api时,TCP数据包是如何到达的#

C# 在C中使用套接字api时,TCP数据包是如何到达的#,c#,sockets,networking,tcp,C#,Sockets,Networking,Tcp,我一直在阅读有关TCP数据包以及如何在航行中多次拆分它们的文章。我认为,为了存储每个ReceiveAsync(),直到有足够的数据可用于解析消息,我必须在用于实际网络流量的缓冲区之上实现某种缓冲区。顺便说一句,我正在通过TCP发送带有长度前缀的protobuf序列化消息 然后我读到下层(以太网?、IP?)实际上会透明地重新组装数据包 我的问题是,在C#中,是否保证通过TCP接收完整的“消息”?换句话说,如果我发送32个字节,我是否需要“一次”接收这32个字节(一次调用ReceiveAsync()

我一直在阅读有关TCP数据包以及如何在航行中多次拆分它们的文章。我认为,为了存储每个
ReceiveAsync()
,直到有足够的数据可用于解析消息,我必须在用于实际网络流量的缓冲区之上实现某种缓冲区。顺便说一句,我正在通过TCP发送带有长度前缀的protobuf序列化消息

然后我读到下层(以太网?、IP?)实际上会透明地重新组装数据包

我的问题是,在C#中,是否保证通过TCP接收完整的“消息”?换句话说,如果我发送32个字节,我是否需要“一次”接收这32个字节(一次调用
ReceiveAsync()
)?或者我必须“存储”每个接收,直到接收的字节数等于长度前缀

另外,我是否可以在对
ReceiveAsync()
的一次调用中收到多条消息?假设一条“protobuf消息”是32字节。我送了两个。我是否可以在“一次”中接收48个字节,然后在另一次中接收16个字节

我知道这个问题很容易在google上出现,但我永远无法判断它是否在正确的上下文中(谈论实际的TCP协议,或者C#将如何向程序员公开网络流量)

谢谢。

TCP是一种流协议-它传输字节流。这就是全部。绝对不隐含任何消息框架/分组。事实上,您应该忘记,在使用TCP套接字编写代码时,甚至存在以太网数据包或IP数据报

您可能会发现自己有1个字节可用,或10000个字节可供读取。(同步)Berkeley sockets API的优点在于,作为应用程序程序员,您不需要担心这一点。由于您使用的是长度前缀消息格式(干得好!),所以只需
recv()
您所期望的字节数即可。如果有比应用程序请求更多的可用字节,内核将在下一次调用之前保持对其余字节的缓冲。如果可用字节数少于所需字节数,线程将阻塞或调用将指示接收的字节数少于所需字节数。在这种情况下,您只需再次休眠,直到数据可用

异步API的问题在于它需要应用程序跟踪更多的状态本身。即使这样,情况也比实际需要复杂得多。使用异步API,您仍然可以控制从内核请求的数据量,但是当启动异步回调时,您需要知道下一个要请求的数据量

请注意,4.5中的C#
异步
/
等待
使异步处理变得更容易,因为您可以以同步方式进行。看看作者的评论:

Socket.ReceiveAsync是一个奇怪的例子。它与.net4.5中的异步/等待功能无关。它被设计为一种替代的套接字API,不会像BeginReceive/EndReceive那样对内存进行重击,只需要在最核心的服务器应用程序中使用

我的问题是,在C#中,是否保证通过TCP接收完整的“消息”

不会。您将不会收到完整的消息。一次发送不会导致一次接收。你必须继续阅读,直到你收到你所需要的一切

请参见此处的示例,它将读取的数据保存在缓冲区中,并不断检查是否有更多数据要读取:

private static void ReceiveCallback(IAsyncResult ar)
{
    try
    {
        // Retrieve the state object and the client socket 
        // from the asynchronous state object.
        StateObject state = (StateObject)ar.AsyncState;
        Socket client = state.workSocket;
        // Read data from the remote device.
        int bytesRead = client.EndReceive(ar);
        if (bytesRead > 0)
        {
            // There might be more data, so store the data received so far.
            state.sb.Append(Encoding.ASCII.GetString(state.buffer, 0, bytesRead));
            //  Get the rest of the data.
            client.BeginReceive(state.buffer, 0, StateObject.BufferSize, 0,
                new AsyncCallback(ReceiveCallback), state);
        }
        else
        {
            // All the data has arrived; put it in response.
            if (state.sb.Length > 1)
            {
                response = state.sb.ToString();
            }
            // Signal that all bytes have been received.
            receiveDone.Set();
        }
    }
    catch (Exception e)
    {
        Console.WriteLine(e.ToString());
    }
}

有关更多详细信息,请参阅MSDN文章和文章。第二个链接介绍了更多细节,它还有示例代码。

TCP是一种基于流的八位字节协议。因此,从应用程序的角度来看,您只能向流中读取或写入字节

我一直在阅读有关TCP数据包以及如何在航行中多次拆分它们的文章

TCP数据包是一个网络实现细节。它们用于提高效率(一次发送一个字节是非常低效的)。数据包分段是在设备驱动程序/硬件级别完成的,并且从不向应用程序公开。应用程序永远不知道“数据包”是什么或其边界在哪里

我认为我必须在用于实际网络流量的缓冲区之上实现某种缓冲区,以便存储每个ReceiveAsync(),直到有足够的数据可用于解析消息

对。因为“消息”不是TCP概念。这纯粹是一个应用程序概念。大多数应用程序协议确实定义了一种“消息”,因为它更容易推理

然而,一些应用程序协议没有定义“消息”的概念;他们将TCP流视为实际流,而不是消息序列

为了支持这两种应用协议,TCP/IP API必须是基于流的

顺便说一句,我正在通过TCP发送带有长度前缀的protobuf序列化消息

那很好。IMO说,长度前缀比其他选项更容易处理

我的问题是,在C#中,是否保证通过TCP接收完整的“消息”

没有

或者我必须“存储”每个接收,直到接收的字节数等于长度前缀?另外,我是否可以在对ReceiveAsync()的一次调用中收到多条消息

对,对

更有趣的是:

  • 您只能获得部分长度前缀(假定为多字节长度前缀)
  • 您可以一次收到任意数量的消息
  • 缓冲区可以包含消息的一部分,也可以包含消息长度前缀的一部分
  • 下次读取可能无法完成当前邮件,甚至无法完成当前邮件的长度前缀
有关详细信息的更多信息,请参见我的,特别是关于和一些的部分

我强烈建议在生产中只使用异步API;每个连接有两个线程的同步替代方案会对可伸缩性产生负面影响

哦,我也总是推荐我们