如何解决Connect超时导致的阻塞问题

这几天发现一个现象,客户端正常连接服务器connect显然不会出现问题。

        在异常情况下,如果是服务器出现异常,connect能够立即返回失败;但是当客户端出现异常的情况下,分为两种情况:

        一种是不插网线,客户端没有获得ip地址,在这种情况下,connect也可以立即返回错误;

        二是但是当客户端插上网线,但是连接网络失败,也就是说能够获取到ip地址,但是和服务器是ping不通的。这种情况下connect就可能会发生阻塞,因为按照《UNIX 网络编程》中讲解,connect的在进行三次握手,如果失败情况,需要等待75s的超市时间的。

        我们主要讨论第二种情况如何解决,可以让connect快速返回结果,不至于阻塞等待超长的时间。

        如下是我的代码

/******************************
* Time out for connect()
******************************/
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <netinet/in.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <sys/time.h>
#include <arpa/inet.h>
#include <fcntl.h>
#include <string.h>
#include <errno.h>
 
 
#define TIME_OUT_TIME 20 //connect超时时间20秒
 
bool setBlockOpt(int m_fd,bool blocked)
{
			
#ifndef WIN32
	int  flags;
	flags = fcntl(m_fd, F_GETFL, 0);
	if(flags < 0)
	{
		return false;
	}
	if(blocked)
	{	
		printf("Set BLOCK !!!\n");
		flags &= ~O_NONBLOCK;
	}
	else
	{
		printf("Set NONBLOCK !!!\n");
		flags |= O_NONBLOCK;
	}
	if(fcntl(m_fd, F_SETFL, flags) < 0)
	{
		return false;
	}
	
#else
	u_long ulValue;
	if(blocked)
	{
		ulValue = 1;
	}
	else
	{
		ulValue = 0;
	}	
	int n = ioctlsocket(m_fd, FIONBIO, &ulValue);
	if (n != 0)
	{
		return false;
	}
#endif
	return true;
}
 
int connectWithTimeout(int m_fd,int timeout)
{
	int selectFlag = -1;
	int error=-1, len;
	len = sizeof(int);
	bool ret = false;
	int connectFlag = -1;
	
	const char* m_ip = "115.239.210.27";
	int m_port = 80;
	
	if("" == m_ip || 0 > m_port)
	{
		return -1;
	}
	
	if(m_fd < 0 && "" != m_ip && m_port >=0)
	{
		m_fd =  socket(AF_INET, SOCK_STREAM, 0);
		if(m_fd < 0)
		{
			return -1;
		}
	}
	
	if(m_fd < 0)
	{
		return -1;
	}
 
	struct sockaddr_in servAddr;
	memset(&servAddr, 0, sizeof(servAddr));
	servAddr.sin_family = AF_INET;
	servAddr.sin_port = htons((unsigned short)m_port);
	servAddr.sin_addr.s_addr = inet_addr(m_ip);
 
	setBlockOpt(m_fd,false);	//设置为非阻塞模式
	if( (connectFlag= connect(m_fd, (struct sockaddr *) &servAddr, sizeof(servAddr)) < 0))
	{
		if(errno != EINPROGRESS)
		{
			goto done;
		}
	}
	else
	{
		ret = true;
		goto done;
	}
	timeval tm;
	tm.tv_sec = timeout/1000;
	tm.tv_usec = timeout%1000;
	fd_set rest, west;
	FD_ZERO(&rest);
	FD_ZERO(&west);
	FD_SET(m_fd, &rest);
	FD_SET(m_fd, &west);
 
	if( (selectFlag = select(m_fd+1, &rest, &west, NULL, &tm)) > 0)
	{
		//如果套接口及可写也可读,需要进一步判断
		if(FD_ISSET(m_fd, &rest) && FD_ISSET(m_fd, &west)) 
		{
			if(getsockopt(m_fd, SOL_SOCKET, SO_ERROR, &error, (socklen_t *)&len) < 0)
			{
				printf("getsockopt error!!!\n");
			}
			else
			{
				if(error == 0)
				{
					ret = true;
				}
				else 
				{
					printf("connect getsockopt error!!! %d\n",error);
				}
			}
		}
		//如果套接口可写不可读,则链接完成
		else if(FD_ISSET(m_fd, &west) && !FD_ISSET(m_fd, &rest)) 
		{ 
			ret = true;
		}
	}
	else if(selectFlag == 0)
	{
		printf("connect select timeout!!!\n");
	}
	else 
	{
		printf("connect select error!!!\n");
	}
	
done:
	setBlockOpt(m_fd,true);// 设置为阻塞模式
	if(!ret)
	{
		return -1;
	}
	return 0;
}
 
 
int main(int argc,char* argv[])
{
	if(argc <= 1)
	{
		printf("input error!!!\n");
		exit(1);
	}
	int sockfd = socket(AF_INET, SOCK_STREAM, 0);
	if(sockfd < 0) 
	{
		exit(1);
	}
	if(connectWithTimeout(sockfd,atoi(argv[1])) == 0)
	{
		printf("connect sucess!!!\n");
	}
	else
	{
		printf("connect filed!!!\n");
	}
	close(sockfd);
	
	
	return 0;
}

       原理很简单,就是先把套接字设置为非阻塞,因为在非阻塞情况下,connect的结果是立即返回的,然后我们再使用select或者poll等机制来检测套接字一定的时间,如果在超时时间内不可写,则认为connect失败,然后需要把套接字重新设置为阻塞,当然如果你不需要在阻塞模式下工作,可以不用设置。

      如上,我们就可以对connect的超时进行可控。

(483条消息) 如何解决Connect超时导致的阻塞问题_clirus的博客-CSDN博客_connect 超时

  • 1
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值