这几天发现一个现象,客户端正常连接服务器connect显然不会出现问题。
在异常情况下,如果是服务器出现异常,connect能够立即返回失败;但是当客户端出现异常的情况下,分为两种情况:
一种是不插网线,客户端没有获得ip地址,在这种情况下,connect也可以立即返回错误;
二是但是当客户端插上网线,但是连接网络失败,也就是说能够获取到ip地址,但是和服务器是ping不通的。这种情况下connect就可能会发生阻塞,因为按照《UNIX 网络编程》中讲解,connect的在进行三次握手,如果失败情况,需要等待75s的超市时间的。
我们主要讨论第二种情况如何解决,可以让connect快速返回结果,不至于阻塞等待超长的时间。
{
int sockfd = socket(AF_INET, SOCK_STREAM, 0);
if(sockfd < 0) exit(1);
struct sockaddr_in serv_addr;
………//以服务器地址填充结构serv_addr
int error=-1, len;
len = sizeof(int);
timeval tm;
fd_set set;
unsigned long ul = 1;
ioctl(sockfd, FIONBIO, &ul); //设置为非阻塞模式
bool ret = false;
if( connect(sockfd, (struct sockaddr *)&serv_addr,
sizeof(serv_addr)) == -1)
{
tm.tv_set = TIME_OUT_TIME;
tm.tv_uset = 0;
FD_ZERO(&set);
FD_SET(sockfd, &set);
if( select(sockfd+1, NULL, &set, NULL, &tm) >
0)
{
getsockopt(sockfd, SOL_SOCKET,
SO_ERROR, &error, (socklen_t *)&len);
if(error == 0) ret = true;
else ret = false;
}
else
ret = false;
}
else
ret = true;
ul = 0;
ioctl(sockfd, FIONBIO, &ul); //设置为阻塞模式
//下面还可以进行发包收包操作
……………
}
sigset(SIGALRM, u_alarm_handler);
alarm(2);
code = connect(socket_fd, (struct sockaddr*)&socket_st,
sizeof(struct sockaddr_in));
alarm(0);
sigrelse(SIGALRM);
-
sigset(SIGALRM, u_alarm_handler);
:这行代码设置了一个信号处理函数u_alarm_handler
,用于处理SIGALRM
信号。当超时时间到达时,操作系统会发送SIGALRM
信号给进程。 -
alarm(2);
:这行代码设置一个定时器,即在2秒钟后发送SIGALRM
信号给进程。这意味着在2秒内,如果连接操作没有完成,就会超时。 -
code = connect(socket_fd, (struct sockaddr*)&socket_st, sizeof(struct sockaddr_in));
:这是连接操作的代码,用于建立与服务器的连接。根据之前的讨论,connect
函数在第二次握手时返回结果。 -
alarm(0);
:这行代码将定时器取消,即停止发送SIGALRM
信号。 -
sigrelse(SIGALRM);
:这行代码解除对SIGALRM
信号的阻塞,即恢复原本的信号处理方式。
void u_alarm_handler()
{
}
connect调用发起第一次握手,在第二次握手返回结果,accept在第三次握手后返回。
第一次握手时,创建连接,connect处于阻塞状态,等待服务器accept,第二次握手成功,connect从阻塞返回,accept同样的需要收到客户端ack以后才从阻塞返回,连接建立完成