【问题标题】:How C++ `recv` function acts at data receving? Could it receive a partial "packet"?C++ `recv` 函数如何在数据接收中起作用?它可以接收部分“数据包”吗?
【发布时间】:2012-02-14 11:04:44
【问题描述】:
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

(我这里说的是应用层数据包,不是传输层。)

整个问题是——我是否需要让客户端有可能将接收到的字节组合成一个数据包或将过度接收的字节拆分到不同的数据包?

【问题讨论】:

  • 那么在 winsock2.h 中的 recv 函数是什么?
  • 我对格式化一无所知。或者这些东西在歌剧中不起作用
  • 好的。下次我会试试的:D
  • 这里最大的问题是TCP、UDP还是SCTP。在 TCP 中:可能,在 UDP/SCTP 中不是。

标签: c++ sockets count byte recv


【解决方案1】:

recv 是否有可能首先获得 500 个字节,而剩下的 12 个将从第二次尝试中获得?

是的,当然。

您无法保证,当发送端发送 X 字节突发时,接收端将在一次调用 recv 时将它们全部接收。 recv 甚至不知道您的应用层“数据包”中有多少字节

整个问题是——我是否需要让客户端有可能将接收到的字节组合成一个数据包或将过度接收的字节拆分到不同的数据包?

您的应用程序肯定必须从可能的顺序读取中累积数据、填充缓冲区并执行解析以查找完整的数据包。

TCP/IP 不知道您的应用程序的协议;正如大卫所说,如果您想将传入的数据流拆分为“数据包”,那么您必须自己进行。

【讨论】:

    【解决方案2】:

    是的,使用 TCP,它可以发生。但这不是问题。如果您收到的太少,请再次致电收到。如果您收到太多,那很好,因为这样可以省去您不得不再次调用 receive 的麻烦。

    网络堆栈知道 TCP,但不知道 正在实施的协议。如果你想把字节流分成消息,那是你的工作。

    如果你不让客户这样做,它怎么可能发生?网络堆栈不知道您的应用层数据包是什么样的。它不知道什么是完整的应用层数据包,因为它不在应用层。

    请注意,这是 TCP 和其他字节流协议的规则。其他协议可能有不同的语义。

    【讨论】:

    • 从您的回答中不清楚这只是 TCP。 UDP 和 SCTP 有数据包的概念,尽管它们不了解更高层
    • @stefaanv 谢谢。我澄清了。
    【解决方案3】:

    在 TCP 通信中,发送方使用write()(可能在循环中)发送数据。在接收方,read() 将接收到的数据从套接字缓冲区复制到应用程序级别的缓冲区中。如果一个 write() 发送 900 字节,TCP 可以将其分成多个不同大小的块......例如300、400和200字节,所以在接收端你需要调用read() 3次才能接收到所有数据。

    现在,如果你把recv() 放在一个循环中,并且每次它填满整个缓冲区或它的一部分,你怎么知道什么时候停止接收?当发件人发送所有数据并正常关闭连接时,您的recv() 将返回0。没有什么要接收的了,你可以关闭你的套接字。

    我提到了在循环中填充缓冲区。如果您没有在recv() 循环中处理来自接收缓冲区的数据,则需要将其保存在某处,否则每次迭代都可能覆盖它。 (您可以在每次迭代中提前缓冲区指针,但这只有在您事先知道数据包的长度时才有效。)您可以将每个接收到的块复制到队列或其他一些数据结构中。数据处理通常与数据接收并行 - 在另一个处理线程中。

    但是让我们回到recv() 循环。除了等待0,还有一个技巧是接收者如何知道何时停止接收:发送者和接收者可以同意(知道)例如发送的前两个字节将携带消息的长度。所以一开始,接收者只会等待两个字节。一旦它收到它们,它将解包消息大小的信息,比如 900 字节。现在接收器可以将其缓冲区大小调整为 900 并循环接收,直到接收完所有 900 个字节。每个recv() 将返回接收到的字节数,并且接收器可以将缓冲区指针推进该字节数,以便下一个recv() 写入缓冲区的空闲部分。

    顺便说一句,客户端和服务器(或接收方和发送方)之间的这种共享知识(合同)是您在应用程序级别的通信协议。它位于 TCP 协议之上。

    【讨论】:

      猜你喜欢
      • 2016-07-23
      • 1970-01-01
      • 2014-01-14
      • 2016-08-10
      • 2015-02-08
      • 2013-02-09
      • 2012-04-23
      • 2017-03-05
      • 2013-07-18
      相关资源
      最近更新 更多