zoukankan      html  css  js  c++  java
  • 关于非阻塞connect的若干细节性问题

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

    1. 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
    ");
    		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
    ");
    			/* 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
    ");
    				ret = 0;
    			}
    			else
    			{
    				//printf("4444444444444444:%d
    ", err);
    				errno = err;
    				ret = -1;
    			}
    		}
    	}
    	if (wait_seconds > 0)
    	{
    		deactivate_nonblock(fd);   //设回阻塞
    	}
    	return ret;
    }
    

      

  • 相关阅读:
    Hibernate缓存之初探
    Oracle、MySql、SQLServer 数据分页查询
    LISTAGG WITHIN GROUP函数使用方法
    exp/imp密码特殊字符处理
    Oracle动态授权用户权限
    Centos7二进制安装Mysql8.0.14
    Zabbix监控Windows网卡流量
    Zabbix监控通过jvm插件监控Tomcat
    Linux安装RabbitMQ3.73开启插件shovel并做配置
    yum安装指定路径和配置文件出错
  • 原文地址:https://www.cnblogs.com/c-slmax/p/5253375.html
Copyright © 2011-2022 走看看