代码之家  ›  专栏  ›  技术社区  ›  Evan Teran

阻塞套接字返回eagain

  •  6
  • Evan Teran  · 技术社区  · 15 年前

    我在Linux上的一个项目使用阻塞套接字。事情是连续发生的,所以不阻塞只会使事情变得更复杂。不管怎样,我经常发现 recv() 呼叫正在返回 -1 具有 errno 设置为 EAGAIN .

    这个 man page只真正提到了非阻塞套接字的情况,这是有意义的。对于非阻塞,套接字可能可用,也可能不可用,因此您可能需要重试。

    什么会导致阻塞套接字发生这种情况?我能做些什么来避免它吗?

    目前,我处理它的代码看起来像这样(我让它在出错时抛出一个异常,但除此之外,它是一个非常简单的包装器 记录() ):

    int ret;
    do {
        ret = ::recv(socket, buf, len, flags | MSG_NOSIGNAL);
    } while(ret == -1 && errno == EAGAIN);
    
    
    if(ret == -1) {
        throw socket_error(strerror(errno));
    }
    return ret;
    

    这是正确的吗? 这个 再次 这种情况经常发生。

    编辑: 我注意到一些可能相关的事情。

    1. 我确实在套接字上设置了一个读取超时,使用 setsockopts() ,但设置为30秒。这个 再次 每30秒发生一次以上。 修正 我的调试有缺陷, 再次 不会像我想象的那样经常发生。可能是超时触发。

    2. 对于连接,我希望能够有连接超时,所以我临时将套接字设置为非阻塞。代码如下:

      int      error = 0;
      fd_set   rset;
      fd_set   wset;
      int      n;
      const SOCKET sock = m_Socket;
      
      // set the socket as nonblocking IO
      const int flags = fcntl (sock, F_GETFL, 0);
      fcntl(sock, F_SETFL, flags | O_NONBLOCK);
      
      errno = 0;
      
      // we connect, but it will return soon
      n = ::connect(sock, addr, size_addr);
      
      if(n < 0) { 
          if (errno != EINPROGRESS) {
              return -1;
          }
      } else if (n == 0) {
          goto done;
      }
      
      FD_ZERO(&rset);
      FD_ZERO(&wset);
      FD_SET(sock, &rset);
      FD_SET(sock, &wset);
      
      struct timeval tval;
      tval.tv_sec = timeout;
      tval.tv_usec = 0;
      
      // We "select()" until connect() returns its result or timeout
      n = select(sock + 1, &rset, &wset, 0, timeout ? &tval : 0);
      if(n == 0) {    
          errno = ETIMEDOUT;
          return -1;
      }
      
      if (FD_ISSET(sock, &rset) || FD_ISSET(sock, &wset)) {
          socklen_t len = sizeof(error);
          if (getsockopt(SOL_SOCKET, SO_ERROR, &error, &len) < 0) {
              return -1;
          }
      } else {
          return -1;
      }
      
      done:
      // We change the socket options back to blocking IO
      if (fcntl(sock, F_SETFL, flags) == -1) {
          return -1;
      }
      return 0;
      

    我的想法是将其设置为非阻塞,尝试连接并在套接字上选择,以便强制执行超时。设置和还原 fcntl() 调用成功返回,因此当该函数完成时,套接字应该再次以阻塞模式结束。

    4 回复  |  直到 15 年前
        1
  •  19
  •   Hasturkun    15 年前

    您可能在套接字上设置了非零接收超时(通过 setsockopt(sock, SOL_SOCKET, SO_RCVTIMEO,...) )因为这也会导致recv返回 EAGAIN

        2
  •  1
  •   max    10 年前

    你有没有可能使用 MSG_DONTWAIT 是否被指定为标志的一部分?这个 man 佩奇说 EAGAIN 如果没有可用的数据并且指定了此标志,则将发生。

    如果你真的想在 recv() 有点成功,您可能希望使用 MSG_WAITALL 旗帜。

        3
  •  0
  •   rmeador    15 年前

    我不建议这是第一次尝试修复,但如果你没有选择,你可以一直 select() 在具有相当长超时的套接字上强制它等待数据。

        4
  •  0
  •   max    10 年前

    EAGAIN 由操作系统生成,几乎就像一个“哎呀!很抱歉打扰你。如果出现此错误,您可以尝试再次读取,这不是严重错误或致命错误。我在Linux和Lynxos中看到过这些中断发生在任何地方,从一天一次到一天100次。