为啥非阻塞套接字在connect() 或accept() 之前是可写的?

Posted

技术标签:

【中文标题】为啥非阻塞套接字在connect() 或accept() 之前是可写的?【英文标题】:Why is nonblocking socket writable before connect() or accept()?为什么非阻塞套接字在connect() 或accept() 之前是可写的? 【发布时间】:2017-12-02 00:56:06 【问题描述】:

从最近的研究中,我了解到如果你想进行非阻塞套接字连接,你可以将你的套接字设置为非阻塞模式,然后使用 select() 并将你的套接字添加到 writefds 参数。

那么,为什么在这个简化的示例中,select() 会提前解除阻塞并留下一个状态,指出我的未连接套接字在我什至没有执行连接时是可写的,更不用说让对等方接受连接了?

#include <iostream>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <fcntl.h>
#include <unistd.h>
#include <cerrno>
#include <cstring>

class SafeSocket

public:

  /** Ctor.
   * Creates a nonblocking socket at the specified IP in the AF_INET family and
   * at a dynamic port.
   */
  SafeSocket( const std::string& ip )
  
    in_addr_t host_ip = inet_network( ip.c_str() );
    if ( ( socket_ = socket( AF_INET, SOCK_STREAM, 0 ) ) < 0 )
    
      std::cout << "socket() failed: " << errno << " " << strerror( errno )
                << std::endl;
      socket_ = -1;
    
    sockaddr_in si;
    memset( &si, 0, sizeof( si ) );
    si.sin_family = AF_INET;
    si.sin_port = 0; // Dynamic port
    si.sin_addr.s_addr = htonl( host_ip );
    if ( bind( socket_, (sockaddr*)&si, sizeof si ) )
    
      std::cout << "bind() failed: " << errno << " " << strerror( errno )
                << std::endl;
      close( socket_ );
      socket_ = -1;
    
    // Make the socket do nonblocking connect().
    int flags = fcntl( socket_, F_GETFL, 0 );
    fcntl( socket_, F_SETFL, flags | O_NONBLOCK );
  

  ~SafeSocket()
  
    if ( socket_ >= 0 )
    
      shutdown( socket_, SHUT_RDWR );
      close( socket_ );
    
  

  operator int() const
  
    return socket_;
  

private:

  int socket_;
;

int main( int argc, char* argv[] )

  SafeSocket s( "127.0.0.100" );
  std::cout << "Created socket " << s << std::endl;

  fd_set readFds;
  fd_set writeFds;

  FD_ZERO( &readFds );
  FD_ZERO( &writeFds );

  FD_SET( s, &writeFds );

  timeval timeout =  5, 0 ;

  if ( -1 == select( s+1, &readFds, &writeFds, NULL, &timeout ) )
  
    std::cout << "select() failed: " << errno << " " << strerror( errno )
              << std::endl;
  

  if ( FD_ISSET( s, &writeFds ) )
  
    std::cout << s << " is writable!" << std::endl;
  

  return 0;

输出:

>g++ --version
g++ (GCC) 4.8.3 20140911 (Red Hat 4.8.3-7)
Copyright (C) 2013 Free Software Foundation, Inc.
This is free software; see the source for copying conditions.  There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.

>g++ -g main.cpp 
>
>./a.out 
Created socket 3
3 is writable!

【问题讨论】:

【参考方案1】:

select() 告诉您套接字何时准备好进行下一步操作。在这种情况下,它已准备好调用connect()

当然,在全新的套接字上调用select() 是不必要的。大多数应用程序不会这样做。

【讨论】:

因此,如果我执行了一个 connect(),并且监听代理需要一段时间来接受该连接,并且我在此“过渡”期间执行了一个 select() - 那应该会阻塞吗? 可以,只要 connect() 返回 -1 并将 errno 设置为 EINPROGRESS。在此处阅读有关处理EINPROGRESS 的更多信息:linux.die.net/man/2/connect 并非如此。这取决于您所说的“监听代理”是什么意思。如果您的意思是服务器应用程序调用accept() 很慢,TCP 将异步完成连接并将其放入侦听器的积压队列中。 select()/connect() 仅在目标 TCP 握手时阻塞。 @EJP - 是的,我的意思是调用accept() 很慢的服务器应用程序。谢谢你,在你和约翰的回答之间,我明白现在发生了什么。具体来说,您的评论解决了我的简化代码和问题背后的问题。

以上是关于为啥非阻塞套接字在connect() 或accept() 之前是可写的?的主要内容,如果未能解决你的问题,请参考以下文章

socket编程 ------ 客户端(非阻塞方式)

为啥套接字不设置为非阻塞模式?

socket 客户端编程:非阻塞式连接,错误判断及退出重连

在linux中连接非阻塞套接字的正确方法是啥

connect() 在阻塞套接字上返回“操作正在进行中”?

非阻塞套接字仍然可以在 OpenSSL 中阻塞吗?