我们用man connection命令查看手册,如下:
 
  • EINPROGRESS
  • The socket is nonblocking and the connection cannot be completed immediately. It
  • is possible to select(2) or poll(2) for completion by selecting the socket for
  • writing. After select(2) indicates writability, use getsockopt(2) to read the
  • SO_ERROR option at level SOL_SOCKET to determine whether connect() completed successfully (SO_ERROR is zero) or unsuccessfully (SO_ERROR is one of the usual error
  • codes listed here, explaining the reason for the failure).
  • 当connect可写时还可能是发生了错误,我们必须判断这种情况。发生错误时套接字既可读又可写,而connect连接成功时套接字也可能即可读又可写(select之前有可能连接已经建立并有来自对端的数据到达).
     
    如何区分这两种情况呢?因此,必须调用

  • getsockopt(sockfd, SOL_SOCKET, SO_ERROR, &error, &len)
  • 另一个问题,对于非阻塞式套接字,如果其上的connect调用被信号中断并且不会由内核自动重启,那么它将返回EINTR,此时我们不能再次调用connect等待未完成的连接继续完成,这样做将导致返回EADDRINUSE错误。我们只能调用select检测其状态,就像上面说的那样。
     
     
    int connect_timeout(int fd, struct sockaddr_in *addr, unsigned int wait_seconds)
    {
    	int ret;
    	socklen_t addrlen = sizeof(struct sockaddr_in);
    
    	if (wait_seconds > 0)
    		activate_nonblock(fd); //设为非阻塞
    
    	ret = connect(fd, (struct sockaddr*)addr, addrlen);
    	if (ret < 0 && errno == EINPROGRESS)
    	{
    		//printf("11111111111111111111\n");
    		fd_set connect_fdset;
    		struct timeval timeout;
    		FD_ZERO(&connect_fdset);
    		FD_SET(fd, &connect_fdset);
    		timeout.tv_sec = wait_seconds;
    		timeout.tv_usec = 0;
    		do
    		{
    			// 一但连接建立,则套接字就可写  所以connect_fdset放在了写集合中
    			ret = select(fd + 1, NULL, &connect_fdset, NULL, &timeout);
    		} while (ret < 0 && errno == EINTR);
    		if (ret == 0)
    		{
    			ret = -1;
    			errno = ETIMEDOUT;
    		}
    		else if (ret < 0)
    			return -1;
    		else if (ret == 1)
    		{
    			//printf("22222222222222222\n");
    			/* ret返回为1(表示套接字可写),可能有两种情况,一种是连接建立成功,一种是套接字产生错误,*/
    			/* 此时错误信息不会保存至errno变量中,因此,需要调用getsockopt来获取。 */
    			int err;
    			socklen_t socklen = sizeof(err);
    			int sockoptret = getsockopt(fd, SOL_SOCKET, SO_ERROR, &err, &socklen);
    			if (sockoptret == -1)
    			{
    				return -1;
    			}
    			if (err == 0)
    			{
    				//printf("3333333333333\n");
    				ret = 0;
    			}
    			else
    			{
    				//printf("4444444444444444:%d\n", err);
    				errno = err;
    				ret = -1;
    			}
    		}
    	}
    	if (wait_seconds > 0)
    	{
    		deactivate_nonblock(fd);   //设回阻塞
    	}
    	return ret;
    }
    

      

    相关文章: