1200字范文,内容丰富有趣,写作的好帮手!
1200字范文 > 如何解决Connect超时导致的阻塞问题

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

时间:2022-05-20 00:45:46

相关推荐

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

版权声明:本文为博主原创文章,遵循 CC 4.0 by-sa 版权协议,转载请附上原文出处链接和本声明。

本文链接:/clirus/article/details/50577352

这几天发现一个现象,客户端正常连接服务器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的超时进行可控。

---------------------

版权声明:本文为CSDN博主「clirus」的原创文章,遵循CC 4.0 by-sa版权协议,转载请附上原文出处链接及本声明。

原文链接:/clirus/article/details/50577352

本内容不代表本网观点和政治立场,如有侵犯你的权益请联系我们处理。
网友评论
网友评论仅供其表达个人看法,并不表明网站立场。