为什么非阻塞套接字在connect()或accept()之前是可写的? [英] Why is nonblocking socket writable before connect() or accept()?

查看:171
本文介绍了为什么非阻塞套接字在connect()或accept()之前是可写的?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

从最近的研究中,我了解到,如果您想进行非阻塞套接字连接,则可以将套接字设置为非阻塞模式,然后使用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!

解决方案

select()告诉您何时套接字准备好进行下一个操作.在这种情况下,可以调用connect().

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

From recent study, I came to the understanding that if you wanted to do a nonblocking socket connect, you could set your socket to nonblocking mode, then use select() with your socket added to the writefds argument.

Why, then, in this simplified example, does select() unblock early and leave a state stating that my unconnected socket is writable when I haven't even performed a connect, let alone had a peer accept the connection?

#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;
}

Output:

>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!

解决方案

select() tells you when a socket is ready for the next action. In this case, it is ready to call connect().

Of course, calling select() on a brand new socket is unnecessary. Most applications wouldn't do that.

这篇关于为什么非阻塞套接字在connect()或accept()之前是可写的?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆