Recvfrom 空缓冲区返回 errno EFAULT?

Recvfrom null buffer returning errno EFAULT?

我正在编写一个 UDP 非阻塞套接字并且我有这个功能

void recvflush(int sockfd) {
  ssize_t num;
  while (errno != EWOULDBLOCK) {
    num = recvfrom(sockfd, NULL, maxpack, 0, NULL, NULL);
    if (num < 0 && errno != EWOULDBLOCK)
      error("recvfrom() failed", strerror(errno));
  }
}

刷新接收缓冲区。但是,当我调用它时它返回 EFAULT(错误地址),并且我确保在我这样做时设置了 sockfd。求助!

谢谢

您是否有理由 期望 NULL 指针指向 recvfrom 会导致它刷新消息?你已经告诉它你的缓冲区是 maxpack 字节长,但是你给了它一个无效的指针,所以 EFAULT 是正确的和预期的 errno.

您应该能够提供零的 length 来丢弃排队的消息:根据 recvfrom(2) 手册页,如果提供的缓冲区长度太短对于一条消息,剩余的数据字节将被丢弃;这应该包括丢弃 all 个字节。

现在,一旦您提供了零长度,您可以提供一个NULL指针,因为不需要缓冲区的任何部分是在那种情况下有效。但这并不是因为 NULL 指针被特别识别,而是因为提供的长度不需要它的任何部分指向有效内存。

根据 recvfrom()man page,在以下情况下报告 EFAULT

EFAULT
The receive buffer pointer(s) point outside the process's address space.

假设您的 maxpack 变量 > 0,recvfrom() 期望能够将字节写入 buf 参数指向的缓冲区,但您正在传递 NULL 该参数,因此出现错误。

为什么要在调用 recvfrom() 之前检查 errno?您应该先调用 recvfrom(),然后检查错误。并在 any 错误 other than EWOULDBLOCK/EAGAIN, EINTR, or [=24] 时停止循环=] 被报道。

此外,请记住 UDP 支持 0 长度数据包,在这种情况下 recvfrom() 将 return 0 而不是 -1,并且不会设置 errno。确保在循环中也考虑到这一点。

试试这个:

void recvflush(int sockfd) {
  ssize_t num;
  do {
    num = recvfrom(sockfd, NULL, 0, 0, NULL, NULL);
    if (num < 0) {
      if ((errno == EWOULDBLOCK) || (errno == EAGAIN))
        break;
      if ((errno != EINTR) && (errno != EMSGSIZE)) {
        error("recvfrom() failed", strerror(errno));
        break;
      }
    }
  }
  while (true);
}

或者,如果 recvfrom() 不接受长度为 0 的缓冲区,则给它一个小缓冲区来写入:

void recvflush(int sockfd) {
  ssize_t num;
  char c;
  do {
    num = recvfrom(sockfd, &c, 1, 0, NULL, NULL);
    if (num < 0) {
      if ((errno == EWOULDBLOCK) || (errno == EAGAIN))
        break;
      if ((errno != EINTR) && (errno != EMSGSIZE)) {
        error("recvfrom() failed", strerror(errno));
        break;
      }
    }
  }
  while (true);
}

请注意,如果您的套接字不是非阻塞的,那么 EWOULDBLOCK/EAGAIN 将不会被报告,因为调用将阻塞直到数据可用,因此您应该使用 select()(e)poll() 在进入 recvfrom() 循环之前检查套接字是否有任何排队的数据包。