TCP超时设置

  在学习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超时

读写超时设置要用到函数setsockopt(),这个比较简单,直接在客户端设置一下就可以了,如下:

tv.tv_sec=3;
tv.tv_usec=0;
setsockopt(sock,SOL_SOCKET,SO_SNDTIMEO,&tv,sizeof(tv));
setsockopt(sock,SOL_SOCKET,SO_RCVTIMEO,&tv,sizeof(tv));

 

你可能感兴趣的:(TCP超时设置)