【发布时间】:2010-12-10 00:58:33
【问题描述】:
如果我在 TCP 中发送 1000 个字节,它是否保证接收者将“一起”获得整个 1000 个字节?或者他可能首先只得到 500 个字节,然后他会收到其他字节?
编辑:问题来自应用程序的角度。如果这 1000 个字节在到达应用程序之前被重新组装成一个缓冲区..那么我不在乎它是否在路上被碎片化了..
【问题讨论】:
标签: c# c++ networking tcp
如果我在 TCP 中发送 1000 个字节,它是否保证接收者将“一起”获得整个 1000 个字节?或者他可能首先只得到 500 个字节,然后他会收到其他字节?
编辑:问题来自应用程序的角度。如果这 1000 个字节在到达应用程序之前被重新组装成一个缓冲区..那么我不在乎它是否在路上被碎片化了..
【问题讨论】:
标签: c# c++ networking tcp
见Transmission Control Protocol:
TCP 提供从一台计算机上的程序到另一台计算机上的另一个程序的可靠、有序的字节流传递。
“流”意味着从接收者的角度来看没有消息边界。您可能会收到一条 1000 字节的消息或一千条 1 字节的消息,具体取决于下面的内容以及您调用 read/select 的频率。
编辑:让我从应用程序的角度澄清一下。不,TCP 不能保证单次读取会为您提供发送方可能已发送的所有 1000 字节(或 1MB 或 1GB)数据包。因此,TCP 之上的协议通常包含固定长度的标头,其中包含总内容长度。例如,您可以始终发送 1 个字节来指示内容的总长度(以字节为单位),最多支持 255 个字节。
【讨论】:
正如其他答案所示,TCP 是一个 stream 协议——发送的每个字节都将被接收(一次且以相同的顺序),但没有内在的“消息边界”——是否所有字节在一个或多个 .send 调用中发送,但仍可能在一个或多个 .receive 调用中接收。
因此,如果您需要“消息边界”,则需要将它们置于 TCP 流(IOW)的顶部,本质上是在应用程序级别。例如,如果您知道要发送的字节永远不会包含\0,则以空字符结尾的字符串可以正常工作;各种“转义”方法可让您发送不受此类限制的字节字符串。 (有现有的协议,但没有一个真正广泛或被广泛接受)。
【讨论】:
基本上,就 TCP 而言,它只保证从一端发送到另一端的数据将以相同的顺序发送。 现在通常你要做的是有一个内部缓冲区,它会一直循环,直到它收到你的 1000 字节“数据包”。 因为前面提到的 recv 命令会返回实际收到的数量。 因此,通常您必须在 TCP 之上实现一个协议,以确保您以适当的速度发送数据。因为如果你一次发送所有数据,它将使底层网络堆栈过载,这将导致复杂化。 所以通常在协议中都会有一个微小的确认包发回来确认1000字节的包已经发送。
【讨论】:
您在您的消息中决定您的消息应包含多少字节。例如,在您的情况下,它是 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:祝您好运。
【讨论】:
是的,有机会部分接收数据包。如果您使用 Windows 套接字,希望这个 msdn article 和以下示例(取自 msdn 中的 article 以便快速查看)对您有所帮助。
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
}
}
【讨论】:
TCP 保证它们将接收所有 1000 个字节,但不一定按顺序接收(尽管它对接收应用程序来说是这样的),也不一定一次全部接收(除非您自己制作数据包并使其如此。)。
也就是说,对于一个小至 1000 字节的数据包,它很有可能会发送一个数据包,只要您在一次调用 send 时发送它,但对于较大的传输它可能不会。
【讨论】:
TCP 层唯一保证的是接收方会收到:
对于如何将字节拆分为“数据包”,根本无法保证。你可能读到的所有关于 MTU、数据包分段、最大段大小或其他任何东西的东西都在 TCP 套接字层之下,并且是无关紧要的。 TCP只提供流服务。
关于您的问题,这意味着接收器可能会收到前 500 个字节,然后是接下来的 500 个字节。或者,接收器可能一次接收一个字节的数据,如果这是它所要求的。这就是recv() 函数接受一个参数的原因,该参数告诉它要返回多少数据,而不是告诉你一个数据包有多大。
【讨论】:
传输控制协议要求接收方确认每个数据包已成功交付给发送方,从而保证所有数据包的成功交付。根据这个定义,当有效负载的大小超过MTU (maximum transmission unit) 时,接收方将始终以块的形式接收有效负载。
更多信息请阅读Transmission Control Protocol。
【讨论】:
IP 数据包在重传过程中可能会出现碎片。
因此目标机器可能会接收到多个数据包——这些数据包将由 TCP/IP 堆栈重新组合回来。根据您使用的网络 API - 数据将被提供给 您 重新组合或以原始数据包的形式。
【讨论】:
这取决于已建立的 MTU(最大传输单元)。如果您建立的连接(一旦握手)指的是 512 字节的 MTU,您将需要两个或更多 TCP 数据包来发送 1000 字节。
【讨论】: