Recvfrom 空缓冲区返回 errno EFAULT?

Posted

技术标签:

【中文标题】Recvfrom 空缓冲区返回 errno EFAULT?【英文标题】:Recvfrom null buffer returning errno EFAULT? 【发布时间】:2018-07-12 23:04:32 【问题描述】:

我正在编写一个 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() 的想法?这就是它崩溃的原因,因为操作系统无法将接收到的数据写入该“地址”。您可能必须分配一个本地缓冲区 (char buf[1500];) 才能读入,即使您只是要将数据丢弃。 想一想,因为多余的数据(来自太大而无法放入用户提供的缓冲区的数据包)被丢弃,我想您可以使用char buf[1] 【参考方案1】:

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

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

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

【讨论】:

啊,好吧。很好的答案,听起来很完美。我只是想我不妨保留maxpack 字节作为参数,因为无论如何我都将它设置为信号处理程序的全局。我想把 0 但一开始我有点怀疑。谢谢!【参考方案2】:

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

失败 接收缓冲区指针指向进程地址空间之外。

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

为什么在调用recvfrom() 之前检查errno?您应该首先调用recvfrom(),然后检查错误。并在报告任何错误除了EWOULDBLOCK/EAGAINEINTREMSGSIZE时停止循环。

另外,请记住,UDP 支持长度为 0 的数据包,在这种情况下,recvfrom() 将返回 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() 循环之前有任何排队的数据包。

【讨论】:

***.com/questions/2178115/…,但是你的情况是便携的没关系(我认为,无限循环有时很棘手)

以上是关于Recvfrom 空缓冲区返回 errno EFAULT?的主要内容,如果未能解决你的问题,请参考以下文章

关于UDP数据报引发“异步错误”的疑问

java BIO/NIO/AIO 学习

I/O模型

一站式学习Java网络编程 全面理解BIO/NIO/AIO

Linux 网络I/O模型

recvfrom() 即使是第一次调用中的 UDP 消息,也不会填充来自 IP 地址