TCP中接收数据

6
如果我在TCP中发送1000个字节,是否保证接收者会“一起”收到整个1000个字节?或者他可能先只收到500个字节,然后稍后再收到其他字节?
编辑:这个问题是从应用程序的角度来看的。如果在到达应用程序之前将1000个字节重新组装成单个缓冲区..那么我不关心它是否以某种方式被分段..
10个回答

15

参见传输控制协议

TCP提供了可靠有序的字节流从一个计算机上的程序到另一个计算机上的程序的传递。

"字节流"意味着接收方看到的没有消息边界。你可能会得到一个1000字节的消息或一千个1字节的消息,这取决于下面的内容以及你调用read/select的频率。

编辑:让我从应用程序的角度来澄清一下。不,TCP不能保证单次读取将给您发送者可能发送的所有1000字节(或1MB或1GB)数据包。因此,通常在TCP之上的协议包含具有总内容长度的固定长度的标头。例如,您可以始终发送一个字节,指示内容的总长度(以字节为单位),这将支持多达255个字节。


或者你可以像SMTP一样做,包含一个终止字符串(. 单独一行)。 - badbod99

3
正如其他答案所指出的,TCP是一种流协议--每个发送的字节都将被接收(一次且按相同顺序),但没有固有的“消息边界”--无论所有字节是否在单个.send调用中发送,还是多个调用中发送,它们仍可能在一个或多个.receive调用中接收到。
因此,如果您需要“消息边界”,则需要在TCP流的顶部强制执行它们,即在应用程序级别上。例如,如果您知道要发送的字节永远不会包含\0,则以null结尾的字符串可以正常工作;各种“转义”的方法让您发送遵循此类限制的字节字符串。(已经存在针对此的协议,但没有真正广泛使用或广泛接受)。

2

您可以决定消息包含多少字节。例如,在您的情况下,它是1000个字节。以下是可用的C#代码来实现相同的功能。该方法返回1000字节。中止代码为0字节;您可以根据需要进行调整。

使用方法:

strMsg = ReadData(thisTcpClient.Client, 1000, out bDisconnected);

以下是方法:

步骤如下:

    string ReadData(Socket sckClient, int nBytesToRead, out bool bShouldDisconnect)
    {
        bShouldDisconnect = false;

        byte[] byteBuffer = new byte[nBytesToRead];
        Array.Clear(byteBuffer, 0, byteBuffer.Length);

        int nDataRead = 0;
        int nStartIndex = 0;

        while (nDataRead < nBytesToRead)
        {

            int nBytesRead = sckClient.Receive(byteBuffer, nStartIndex, nBytesToRead - nStartIndex, SocketFlags.None);

            if (0 == nBytesRead)
            {
                bShouldDisconnect = true;
                //0 bytes received; assuming disconnect signal
                break;
            }

            nDataRead += nBytesRead;
            nStartIndex += nBytesRead;
        }

        return Encoding.Default.GetString(byteBuffer, 0, nDataRead);
    }

如果这篇文章没有帮到您,请告诉我们 (0: 祝您好运。


2
基本上,就TCP而言,它只保证从一端发送到另一端的数据将按相同顺序发送。通常情况下,您需要使用内部缓冲区,不断循环直到接收到1000字节的“数据包”为止。因为recv命令会返回实际接收到的数据量。所以通常你需要在TCP之上实现一个协议,以确保以适当的速度发送数据。因为如果您一次性send()所有数据,它将超载底层网络堆栈,从而引起复杂的问题。所以通常在协议中会发送一个小的确认数据包来确认发送了1000字节的数据包。

1

是的,有可能分批接收数据包。如果您正在使用Windows套接字,请参考MSDN文章和以下示例(摘自MSDN文章以供快速查看),希望对您有所帮助。

void CChatSocket::OnReceive(int nErrorCode)
{
   CSocket::OnReceive(nErrorCode);

   DWORD dwReceived;

   if (IOCtl(FIONREAD, &dwReceived))
   {
      if (dwReceived >= dwExpected)   // Process only if you have enough data
         m_pDoc->ProcessPendingRead();
   }
   else
   {
      // Error handling here
   }
}

0

TCP保证接收所有1000字节,但不一定按顺序(尽管对于接收应用程序来说似乎是这样),也不一定一次性接收完毕(除非您自己构造数据包并使其如此)。

话虽如此,对于像1000字节这样小的数据包,只要您在一次send调用中发送,它很有可能会一次性发送完毕,但对于更大的传输可能不会。


从TCP客户端的角度来看,TCP确保按顺序传递数据报。底层的IP没有这个保证,因此可能会重新排序它们,但这是TCP的内部细节。 - Eugene Yokota
数据包也是如此。但 OP 对它们感到担忧。你是对的,我在说接收应用程序将始终按顺序接收它们时已经提到了这一点。但它们不一定按顺序传递到计算机。 - Matthew Scharley

0
TCP层能够保证的唯一一件事情就是接收方会按照相同的顺序接收到发送方传输的全部字节。关于这些字节如何被分割成“数据包”,TCP层并不提供任何保证。你可能读到有关最大传输单元、数据包分段、最大分段大小或其他类似内容的文章,但这些都在TCP套接字层之下,是无关紧要的。TCP仅提供流服务。
就你的问题而言,这意味着接收方可能会先接收前500个字节,然后稍后再接收下一个500个字节。或者,如果接收方要求以此方式进行,则可以逐个字节地接收数据。这就是recv()函数需要参数来指定要返回多少数据,而不是告诉你数据包有多大的原因。

-1
传输控制协议通过要求接收方向发送方确认每个数据包的成功传递,以保证所有数据包的成功传递。根据这个定义,当负载大小超过MTU(最大传输单元)时,接收方将总是以块的形式接收负载。
欲了解更多信息,请阅读传输控制协议

“Packet size”指的是例如IPv4的限制为65,507字节吗? - unknown
抱歉,我是指MTU - 我已经编辑了我的答案以反映这一点。 - Andrew Hare
TCP保证数据的成功传递,但不保证分组 - 请参见我关于重传的回复。 - Zepplock

-1

在重传过程中,IP数据包可能会被分段。

因此,目标机器可能会接收到多个数据包 - 这些数据包将由TCP/IP堆栈重新组装。根据您使用的网络API,数据将以重新组装或原始数据包的形式提供给


-1

这取决于已建立的MTU(最大传输单元)。如果您已建立的连接(一旦握手)参考了512字节的MTU,则需要两个或更多TCP数据包才能发送1000字节。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接