C++ 非阻塞套接字 - 等待所有接收数据

C++ nonblocking sockets - wait for all recv data

我没有 运行 在我的本地系统上遇到这个问题(当然),但现在我正在设置一个虚拟服务器,我的部分代码遇到了一些问题。

为了从非阻塞 TCP recv() 接收所有数据,我有这个函数

ssize_t Server::recvAll(int sockfd, const void *buf, size_t len, int flags) {
    // just showing here that they are non-blocking sockets
    u_long iMode=1;
    ioctlsocket(sockfd,FIONBIO,&iMode);

   ssize_t result;
   char *pbuf = (char *)buf;
   while ( len > 0 ) {
     result = recv(sockfd,pbuf,len,flags);
     printf("\tRES: %d", result);
     if ( result <= 0 ) break;
     pbuf += result;
     len -= result;
   }

   return result;
}

我注意到 recvAll 通常 打印 RES: 1024 (1024 是我发送的字节数)并且效果很好。但不太常见的是,数据丢失并且它只打印 RES: 400(其中 400 是大于 0 且小于 1024 的某个数字)并且我的代码不起作用,因为它需要所有 1024 个字节。

我也试过在调试中打印 WSAGetLastError() 和 运行,但由于 print/debug,我没有遇到这个问题,所以看起来程序运行很慢.

我认为这个函数对阻塞套接字非常有效,但对非阻塞套接字无效。

关于我可以采取的测量的任何建议,以确保我在非阻塞套接字上接收到所有 1024 字节而没有数据丢失?

如果你使用非阻塞模式,那么你会读取所有已经到达系统的数据。一旦你读出所有数据 recv returns 错误,原因取决于系统:

  • EWOULDBLOCK(在posix系统中)
  • windows 套接字系统中的 WSAEWOULDBLOCK

收到此错误后,您需要等待另一个数据的到来。您可以通过多种方式做到这一点:

  • 等待 select/poll/epoll
  • 等特殊功能
  • 休眠一段时间并尝试再次接收(用户-space 轮询)

如果需要减少延迟,select/poll/epoll 更可取。睡眠更容易实现。

您还需要考虑 TCP 是流协议并且不会保持帧。这意味着您可以发送 256 个字节,然后再发送 256 个字节,但一次接收 512 个字节。这也适用于相反的方式:您可以一次发送 512 个字节,并在第一次读取时接收 256 个字节,在下一次读取时接收另外 256 个字节。