C++ 如何`recv`函数作用于数据接收?它能收到部分“数据包”吗?
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
(我在这里讨论的是应用层数据包,而不是传输层。)
整个问题是 - 我是否需要为客户端提供某种可能性,将接收到的字节组合成一个数据包或拆分过度接收的字节字节到不同的数据包?
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());
}
For now, it only prints the amount of bytes received.
Could such things happen, that recv
will receive only a half of packet? Or one full packet and half of next packet, if server sent them one by one fast?
For example, server sent a single packet with 512 bytes length, is it possible that recv first got 500 bytes, and the remain 12 will receive from second attempt?
If server sending a lot of packets with 512 bytes length for each, is it possible that recv
will get 700 bytes from first executing and the remain bytes from second?
MAX_PACKET_LENGTH
is 1024
(I talk here about application layer packets, not transport layer.)
The whole problem is - do I need to make some possibility for client to combine received bytes into one packet or split over-received bytes to different packets?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(3)
是的,绝对是。
您无法保证,当发送端发送 X 个字节的突发时,接收端将在一次调用
recv
中将它们全部接收。recv
甚至不知道您的应用程序层“数据包”中有多少字节。您的应用程序肯定必须从可能的顺序读取中积累数据,填充缓冲区,并实现解析以查找完整的数据包。
TCP/IP 不知道您的应用程序的协议;正如 David 所说,如果您想将传入的数据流拆分为“数据包”,那么您必须自己执行此操作。
Yes, definitely.
You have no guarantee that, when the sending end sends a burst of X bytes, that the receiving end will pick them all up in a single call to
recv
.recv
doesn't even know how many bytes are in your application-layer "packet".Your application will definitely have to accumulate data from possibly sequential reads, fill up a buffer, and implement parsing to look for a full packet.
TCP/IP doesn't know your application's protocol; as David said, if you want to split the incoming data stream into "packets" then you must do that yourself.
是的,使用 TCP,这可能会发生。但这不是问题。如果您收到的太少,请再次致电 receive。如果您收到太多,那就太好了,因为它可以省去您再次调用 receive 的麻烦。
网络堆栈知道 TCP,但它不知道您正在实现的协议。如果您想将字节流划分为消息,那就是您的工作。
如果你不让客户这样做,这怎么可能发生?网络堆栈不知道您的应用程序层数据包是什么样的。它不知道什么构成完整的应用程序层数据包,因为它不在应用程序层。
请注意,这是 TCP 和其他字节流协议的规则。其他协议可能具有不同的语义。
Yes, with TCP, it can happen. But it's not a problem. If you receive too little, call receive again. If you receive too much, well that's great because it just saves you the trouble of having to call receive again.
The networking stack knows TCP, but it doesn't know the protocol you are implementing. If you want to divide the byte stream into messages, that's your job.
If you don't make the client do it, how will it possibly happen? The networking stack has no idea what your application layer packets are like. It has no idea what constitutes a complete application layer packet since it's not at the application layer.
Note that is the rule for TCP and other byte-stream protocols. Other protocols may have different semantics.
在 TCP 通信中,发送方使用 write()(可能在循环中)发送数据。在接收端,
read()
将接收到的数据从套接字缓冲区复制到应用程序级别的缓冲区中。如果一个 write() 发送了 900 字节,TCP 可以将其分成多个不同大小的块...例如 300、400 和 200 字节,因此在接收端您需要调用read() 三次才能接收所有数据。
现在,如果您将
recv()
放入一个循环中,并且每次它都会填充整个缓冲区或其一部分,您如何知道何时停止接收?当发送方发送所有数据并正常关闭连接时,您的recv()
将返回0
。没有更多内容可以接收,您可以关闭套接字。我提到过在循环中填充缓冲区。如果您没有在
recv()
循环中处理来自接收缓冲区的数据,则需要将其保存在某处,否则每次迭代都可能会覆盖它。 (您可以在每次迭代中提前缓冲区指针,但这只有在您提前知道数据包的长度时才有效。)您可以将每个接收到的块复制到队列或其他数据结构中。数据处理通常与数据接收并行进行 - 在另一个处理线程中。但让我们回到
recv()
循环。除了等待0
之外,接收方如何知道何时停止接收还有另一个技巧:发送方和接收方可以同意(知道)例如发送的前两个字节将携带长度消息。所以一开始,接收方只会等待两个字节。一旦收到它们,它将解压缩有关消息大小的信息,假设为 900 字节。现在接收方可以将其缓冲区大小调整为 900 并循环接收,直到接收到所有 900 个字节。每个recv()
将返回接收到的字节数,接收者可以将缓冲区指针前进该字节数,以便下一个recv()
写入缓冲区的空闲部分。顺便说一句,客户端和服务器(或接收者和发送者)之间的共享知识(契约)是应用程序级别的通信协议。它位于 TCP 协议之上。
In TCP communication sender uses
write()
(possibly in a loop) to send data. On the receiver side,read()
copies received data from a socket buffer into your buffer at application level. If one write() sends let's say 900 bytes, TCP can break it into multiple chunks of various sizes...e.g. 300, 400, and 200 bytes, so on the receiving side you need to callread()
three times in order to receive all data.Now, if you put
recv()
in a loop and each time it fills entire buffer or its part, how do you know when to stop receiving? When sender sends all data and gracefully closes connection, yourrecv()
will return0
. There is nothing more to receive, you can close your socket.I mentioned filling the buffer in a loop. If you're not processing data from receiving buffer in the
recv()
loop, you need to preserve it somewhere, otherwise each iteration might overwrite it. (You can advance buffer pointer in each iteration but that would work only if you know in advance the length of the packet.) You can copy each received chunk into queue or some other data structure. Data processing usually goes in parallel with data receiving - and in another, processing thread.But let's go back to
recv()
loop. Apart from waiting for0
, there is another trick of how can receiver know when to stop receiving: sender and receiver can agree (know) that e.g. first two bytes sent will carry the length of the message. So at the beginning, receiver will wait only for two bytes. Once it receives them, it will unpack the information on the message size, let's say 900 bytes. Now receiver can adjust its buffer size to 900 and receives in a loop until all 900 bytes are received. Eachrecv()
will return number of bytes received, and receiver can advance buffer pointer by that number of bytes so the nextrecv()
writes into free part of the buffer.Btw, this shared knowledge (contract) between client and server (or receiver and sender) is your communication protocol at the application level. It comes on the top of TCP protocol.