static void HandlePackets(void* pParams)
{
int iResult = 0;
char recvbuf[MAX_PACKET_LENGTH];
printf("Packet handling started\n");
while((iResult = recv(lhSocket, recvbuf, MAX_PACKET_LENGTH, 0)) > 0)
printf("Bytes received: %d\n", iResult);
printf("Packet handling stopped with reason %i", WSAGetLastError());
}
目前,它只打印接收的字节数。
这样的事情会发生吗,recv
只收到一半数据包?或者一个完整的数据包和下一个数据包的一半,如果服务器一个接一个地快速发送它们?
例如,服务器发送了一个长度为512字节的数据包,recv可能先获得500字节,剩下的12会从第二次尝试中获得吗?
如果服务器为每个发送大量512字节长度的数据包,那么recv
可能从第一次执行时获得700字节而从第二次执行中剩余字节是什么?
MAX_PACKET_LENGTH
是1024
(我在这里谈论应用层数据包,而不是传输层。)
整个问题是 - 我是否需要让客户端将接收到的字节组合成一个数据包或将过度接收的字节分成不同的数据包?
答案 0 :(得分:3)
是的,使用TCP,它可能会发生。但这不是问题。如果收到的太少,请再次致电。如果你得到太多,那就太好了,因为它只会让你省去不得不再次拨打接收的麻烦。
网络堆栈知道TCP,但它不知道您正在实施的协议。如果你想将字节流分成消息,这就是你的工作。
如果你不让客户这样做,它会怎么可能发生?网络堆栈不知道您的应用程序层数据包是什么样的。它不知道什么构成了完整的应用层数据包,因为它不在应用层。
请注意,这是TCP和其他字节流协议的规则。其他协议可能有不同的语义。
答案 1 :(得分:3)
recv
是否有可能首先获得500个字节,剩余的12个是否会从第二次尝试中获得?
是的,当然。
当发送端发送一个X字节的突发时,您无法保证接收端只需一次调用recv
即可全部接收它们。 recv
甚至不知道 你的应用层“数据包”中有多少字节。
整个问题是 - 我是否需要让客户端将接收到的字节组合成一个数据包或将过度接收的字节分成不同的数据包?
您的应用程序肯定必须从可能的顺序读取中累积数据,填充缓冲区,并实现解析以查找完整的数据包。
TCP / IP不知道您的应用程序的协议;正如David所说,如果你想将传入的数据流分成“数据包”,那么你必须自己做。
答案 2 :(得分:2)
在TCP通信中,发件人使用write()
(可能在循环中)发送数据。在接收方,read()
将接收到的数据从套接字缓冲区复制到应用程序级别的缓冲区中。如果一个write()发送让我们说900字节,TCP可以将它分成多个不同大小的块...例如。 300,400和200字节,因此在接收方,您需要调用read()
三次才能接收所有数据。
现在,如果你将recv()
放在循环中,并且每次填充整个缓冲区或其部分时,你怎么知道何时停止接收?当发件人发送所有数据并正常关闭连接时,您的recv()
将返回 0
。没有什么可以收到的,你可以关闭你的插座。
我提到在循环中填充缓冲区。如果您没有在recv()
循环中处理来自接收缓冲区的数据,则需要将其保留在某处,否则每次迭代都可能会覆盖它。 (您可以在每次迭代中提前缓冲区指针,但只有在事先知道数据包的长度时才能使用。)您可以将每个接收到的块复制到队列或其他一些数据结构中。数据处理通常与数据接收并行 - 在另一个处理线程中。
但是让我们回到recv()
循环。除了等待0
之外,还有另一个技巧,即接收者如何知道何时停止接收:发送者和接收者可以同意(知道)例如发送的前两个字节将带有消息的长度。所以在开始时,接收器将只等待两个字节。一旦收到它们,它将解压缩消息大小的信息,比方说900字节。现在,接收器可以将其缓冲区大小调整为900并在循环中接收,直到收到所有900个字节。每个recv()
将返回接收的字节数,接收器可以按该字节数提前缓冲区指针,以便下一个recv()
写入缓冲区的空闲部分。
顺便说一句,客户端和服务器(或接收方和发送方)之间的共享知识(合同)是您在应用程序级别的通信协议。它位于TCP协议之上。