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

来源:互联网 发布:java接口文档怎么写 编辑:程序博客网 时间:2024/04/26 03:49

        这几天发现一个现象,客户端正常连接服务器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 WIN32int  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;}#elseu_long ulValue;if(blocked){ulValue = 1;}else{ulValue = 0;}int n = ioctlsocket(m_fd, FIONBIO, &ulValue);if (n != 0){return false;}#endifreturn 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的超时进行可控。

1 0
原创粉丝点击