C 套接字编程中的 recv() 不是阻塞的吗?

sat*_*ttu 3 c sockets recv

在接收器中,我有

recvfd=accept(sockfd,&other_side,&len);
while(1)
{
   recv(recvfd,buf,MAX_BYTES-1,0);
   buf[MAX_BYTES]='\0';
   printf("\n Number %d contents :%s\n",counter,buf);
   counter++;
}
Run Code Online (Sandbox Code Playgroud)

在 Sender ,我有

send(sockfd,mesg,(size_t)length,0);
send(sockfd,mesg,(size_t)length,0);
send(sockfd,mesg,(size_t)length,0);
Run Code Online (Sandbox Code Playgroud)

MAX_BYTES 为 1024,消息长度为 15。目前,它只调用一次 recv。我希望为每个相应的发送调用 recv 函数三次。我如何实现它?

eck*_*kes 6

简而言之:是,它是阻塞的。但不是你想的那样。

recv()阻塞直到任何数据可读。但你事先并不知道尺寸。

在您的场景中,您可以执行以下操作:

  1. 调用select()并将要读取的套接字放入 READ FD 集中
  2. select()返回正数时,您的套接字已准备好读取数据
  3. 然后,检查是否可以length从套接字接收字节:
    recv(recvfd, buf, MAX_BYTES-1, MSG_PEEK),查看man recv(2)参数MSG_PEEK或查看 MSDN,他们也有它
  4. 现在您知道有多少数据可用
  5. 如果可用的数量少于length可用数量,则返回并且不执行任何操作
  6. 如果至少有length可用的,则读取length并返回(如果有超过length可用的,我们将继续步骤 2,因为新的 READ 事件将由select()


Kaz*_*Kaz 5

要通过字节流协议发送离散消息,您必须将消息编码为某种帧语言。网络可以将协议分成任意大小的数据包,因此接收的消息与您的消息没有任何关联。接收器必须实现一个识别帧的状态机。

一个简单的成帧协议是有一些长度字段(比如两个八位字节:16 位,最大帧长度为 65535 字节)。长度字段后跟正是那么多字节。

您甚至不能假设长度字段本身是一次性收到的。您可能会要求两个字节,但recv可以只返回一个。对于从套接字接收到的第一条消息,这不会发生,因为网络(或本地 IPC 管道,就此而言)段永远不会只有一个字节长。但是在流中间的某个地方,16 位长度字段的第一个字节可能会落在一个网络帧的最后一个位置。

解决这个问题的一个简单方法是使用缓冲 I/O 库而不是原始操作系统文件句柄。在 POSIX 环境中,您可以获取一个打开的套接字句柄,并使用该fdopen函数将其与FILE *流相关联。然后你可以使用像getcfread这样的函数来简化输入处理(有点)。

如果带内成帧是不可接受的,那么您必须使用支持成帧的协议,即数据报类型套接字。这样做的主要缺点是 IP 上使用的主要基于数据报的协议是 UDP,而 UDP 不可靠。这给您的应用程序带来了很多复杂性,以处理无序和丢失的帧。帧的大小还受到最大 IP 数据报大小的限制,该数据报大小约为 64 KB,包括所有协议标头。

大的 UDP 数据报会被分段,如果网络中存在不可靠性,这会增加更大的不可靠性:如果任何 IP 分段丢失,整个数据包都会丢失。所有这些都必须重新传输;没有办法只获得丢失的片段的重复。TCP 协议执行“路径 MTU 发现”以调整其分段大小以避免 IP 分片,并且 TCP 具有选择性重传以恢复丢失的分段。


Mat*_*ser -4

recv将阻塞直到整个缓冲区被填满,或者套接字被关闭。

如果您想读取length字节并返回,则必须仅传递给recvsize 的缓冲区length

您可以使用select来确定是否

  1. 有任何字节等待读取,
  2. 有多少字节等待读取,那么
  3. 只读取那些字节

这样可以避免recv阻塞。

编辑:

重新阅读文档后,以下情况可能是正确的:自 以来,您的三个“消息”可能会被一次性读取length + length + length < MAX_BYTES - 1

另一种可能性(如果recv永远不会返回)是您可能需要flush从发送方访问您的套接字。数据可能在缓冲区中等待实际发送到接收器。

  • 这并不完全正确。`recv()` 将阻塞,直到*任何*数据可用,然后它返回实际接收到的字节数*不超过请求的字节数*,因此 `recv()` 有可能(甚至很可能)返回比请求的字节少。它不会等待整个请求的号码到达才退出。这就是为什么关注实际返回值并在需要更多字节时再次调用“recv()”非常重要。 (2认同)