在学习TCP超时设置的时候,发现网上没有完整的超时介绍,遂总结一下。TCP超时总共分为3类:connectTimeout, writeTimeout, readTimeout(连接超时,读超时,写超时)。下面分别介绍如何设置这三种超时。
1. 连接超时
在TCP调用connect函数时,TCP的建立需要3次握手,从客户端发出SYS信号之后开始等待,超过超时时间即连接失败,connect函数不再等待,直接返回。这个时间称为超时时间。超时时间系统是有最大限制的,以Linux系统为例,调用命令:sysctl net.ipv4.tcp_syn_retries可以查看系统设置的connectTimeout最大值。返回值:
4:timeout是31s
5: timeout是75s
6: timeout是127s
但是有时我们希望设置自己的connectTimeout时间(注:此时间必须比系统timeout时间短,否则系统会截成系统timeout时间)。
1.1 使用alarm函数
在设置超时时间时,可以采用alarm函数,具体如下:
//超时处理函数
void alarm_handler(int sig)
{
printf("connect timeout");
return;
}
int main()
{
...
signal(SIGALRM, alarm_handler)
alarm(5); //设置超时时间5s
int rc=connect(...); //调用connect函数
alarm(0);
if(rc<0){
if(errno==EINTR){
//connect超时
}
}
}
这种方法可能有以下缺陷:
(1) 有些UNIX操作系统在信号处理程序返回之后可能重启connect调用;
(2) 假如connect成功,但是此时alarm定时到了,此时程序仍然会终止。
1.2 使用select函数
使用select函数,监听套接字是否有读或写性质的变化(实际上监视写性质的变化就行了,因为一旦连接成功,套接字一定是可写的)。下面看伪代码:
1 int main()
2 {
3 int sock;
4
5 sock=socket(AF_INET,SOCK_STREAM,0); //1.调用socket函数
6
7 //2. 将sock设置成非阻塞
8
9 //3.调用connect函数
10 int rc=connect(sock,...);
11 /*****************
12 调用connect函数后,因为设置成非阻塞,会有三种典型情况:
13 1. rc=0,连接成功
14 2. rc!=0 && errno=EINPROCRESS,说明还未连接成功
15 3. rc!0 && errno=!EINPROCRESS,连接失败
16 ******************/
17 if(rc==0){
18 连接成功,将sock设置成阻塞;
19 执行后续客户端程序;
20 }
21 else if(rc!=0 && error!=EINPROCRESS)
22 连接失败,直接返回;
23 else{
24 fd_set rdevent,wrevent,exevent; //这里可以只检测写事件,即wrevent
25 FD_ZERO(&rdevent);
26 FD_SET(sock,&rdevent);
27 wr=rdevent;
28 exevent=rdevent; //设置读监视,写监视以及异常监视
29 tv.tv_sec=5;
30 tv.tv_usecc=0; //设置超时时间5s,此部分相关操作均可在select函数使用方法中查询
31 rc=select(sock+1,&rdevent,&wrevent,&exevent,&tv);
32 if(rc<0)
33 select函数错误;
34 else if(rc==0)
35 select函数超时,即连接超时connectTimeout
36 else{ //有监测信号返回
37 //此时检测是否是连接成功
38 if(!FD_ISSET(sock,&rdevent) && !FD_ISSET(sock,&wrevent)) //既不可读,也不可写,一定是连接错误
39 int err;
40 int len=sizeof(err);
41 if(getsockopt(sock,SOL_SOCKET,SO_ERROR,&err,&len)<0)
42 调用getsockopt()函数本身的错误;
43 if(err!=0)
44 说明连接错误,退出;
45 else
46 连接成功,设置sock为阻塞,开始客户端处理函数;
47 }
48 }
49 }
最关键的判断有两处,一是调用connect()函数后有三种可能情况,见上面12-15行;二是slelect检测到了性质变化,调用getsockopt()函数,如果返回的错误err=0,说明没错,连接建立,否则连接没建立,见上面41-46行。
注:若conenct函数调用失败之后,不能马上再次调用connect函数,必须先关闭套接字。
2. writeTimeout和readTimeout超时
来源:https://www.cnblogs.com/yuanwebpage/p/12443825.html