论坛交流
首页办公自动化| 网页制作| 平面设计| 动画制作| 数据库开发| 程序设计| 全部视频教程
应用视频: Windows | Word2007 | Excel2007 | PowerPoint2007 | Dreamweaver 8 | Fireworks 8 | Flash 8 | Photoshop cs | CorelDraw 12
编程视频: C语言视频教程 | HTML | Div+Css布局 | Javascript | Access数据库 | Asp | Sql Server数据库Asp.net  | Flash AS
当前位置 > 文字教程 > C语言程序设计教程
Tag:新手,函数,指针,数据类型,对象,Turbo,入门,运算符,数组,结构,二级,,tc,游戏,试题,问答,编译,视频教程

connect超时时间的一点探讨

文章类别:C语言程序设计 | 发表日期:2008-9-24 14:37:47

前言:
对阻塞的connect到底会多久超时(返回-1,并且errno被设为ETIMEDOUT)一直也没有搞清楚,今天花时间
看了一下代码并作了一点实验,大致得出了一点结论。没有时间写的太细了,把结果贴出来,感爱好的人自己
去看吧。

背景知识:
各种系统对此都没有一个总时间的限制,而是设置了重连的次数(即假如收不到synack,会重试多少遍),这个缺
省值个个系统不大一样(linux不同版本这个值也有过变化,见后)。每次重连之间的间隔时间会通过算法来调整,
这个算法个个系统的实现也有差别,因此造成了系统之间connect超时时间的千差万别(早期的BSd系统会共耗时75秒
,这点《UNP》 section4.3中提到了,但现代的系统一般更久)。

大多数系统这个重连数可调节(linux可以通过/proc/sys/net/ipv4/tcp_syn_retries或sysctl来修改此值。
《TCP/IP V1》的附录E提到了其他系统的)

底下简单分析了linux下的相关代码,并作了个小实验。为了让timeout能够超时,我设了防火墙(通过ipchains)
,将规则设为DENY(REJECT会返回icmp端口不可达,会造成connect函数马上返回),并通过tcpdump检查发包重试
的情况。

自己写程序时应该考虑这种情况,自行设置更短超时(《UNP》中说的很清楚了)。

nmap再扫描这类没有任何返回的端口时,会将其状态标为"filter",这时大多情况就是有防火墙了(假如带宽足够,
对方系统又没有“非常忙”的情况下)。

更细致的探讨请见rfc793,rfc1122,《TCP/IP V1》和论文《Congestion Avoidance and Control》。

还有一个疑问,为什么alan cox在两次邮件中一次说总超时大约是15分钟,一次又说是30分钟?是不是以前这个重试
次数设的更大?没时间考古了,知道的请回我的mail:yawl@docshow.net.相关连接如下:
http://www.wcug.wwu.edu/lists/netdev/199808/msg00032.html
http://www.uwsg.indiana.edu/hypermail/linux/kernel/9802.0/0384.html

linux相关的代码(结合后面的结论看):
tcp_v4_init_sock():
tp->rto = TCP_TIMEOUT_INIT; //初值,即3秒

tcp_retransmit_timer():
tp->retransmits++;
tp->rto = min(tp->rto << 1, TCP_RTO_MAX); //假如小于两分钟(TCP_RTO_MAX),每次乘2
tcp_reset_xmit_timer(sk, TCP_TIME_RETRANS, tp->rto);

tcp_connect():
tp->rto = TCP_TIMEOUT_INIT; //重复语句???
tp->retransmits = 0;
tcp_clear_retrans(tp);

运行结果:
[yawl@redhat-6 tmp]$ gcc -o connect connect.c
[yawl@redhat-6 tmp]$ ./connect 192.168.10.1 50001
Fri Apr 20 12:37:36 2001
connect fail: Connection timed out
Fri Apr 20 12:50:45 2001

运行环境如下:
[yawl@redhat-6 tmp]$ uname -a
Linux redhat-6 2.2.12-20 #1 Mon Sep 27 10:40:35 EDT 1999 i686 unknown
[yawl@redhat-6 tmp]$ cat /proc/sys/net/ipv4/tcp_syn_retries
10
[root@redhat-6 yawl]# ipchains -A input --proto tcp --destination-port 50001 -j DENY
[root@redhat-6 yawl]# ipchains -L
Chain input (policy ACCEPT):
target prot opt source destination ports
DENY tcp ------ anywhere anywhere any -> 50001
Chain forward (policy ACCEPT):
Chain output (policy ACCEPT):
[root@redhat-6 yawl]# tcpdump port 50001
Kernel filter, protocol ALL, datagram packet socket
tcpdump: listening on all devices
12:37:36.926670 lo > redhat-6.1.nsfocus.com.3389 > redhat-6.1.nsfocus.com.50001: S 2996115497:2996115497(0) win 31072 <mss 3884> (DF)
12:37:36.926670 lo < redhat-6.1.nsfocus.com.3389 > redhat-6.1.nsfocus.com.50001: S 2996115497:2996115497(0) win 31072 <mss 3884> (DF)
12:37:39.924937 lo > redhat-6.1.nsfocus.com.3389 > redhat-6.1.nsfocus.com.50001: S 2996115497:2996115497(0) win 31072 <mss 3884> (DF)
12:37:39.924937 lo < redhat-6.1.nsfocus.com.3389 > redhat-6.1.nsfocus.com.50001: S 2996115497:2996115497(0) win 31072 <mss 3884> (DF)
12:37:45.924934 lo > redhat-6.1.nsfocus.com.3389 > redhat-6.1.nsfocus.com.50001: S 2996115497:2996115497(0) win 31072 <mss 3884> (DF)
12:37:45.924934 lo < redhat-6.1.nsfocus.com.3389 > redhat-6.1.nsfocus.com.50001: S 2996115497:2996115497(0) win 31072 <mss 3884> (DF)
12:37:57.924934 lo > redhat-6.1.nsfocus.com.3389 > redhat-6.1.nsfocus.com.50001: S 2996115497:2996115497(0) win 31072 <mss 3884> (DF)
12:37:57.924934 lo < redhat-6.1.nsfocus.com.3389 > redhat-6.1.nsfocus.com.50001: S 2996115497:2996115497(0) win 31072 <mss 3884> (DF)
12:38:21.924940 lo > redhat-6.1.nsfocus.com.3389 > redhat-6.1.nsfocus.com.50001: S 2996115497:2996115497(0) win 31072 <mss 3884> (DF)
12:38:21.924940 lo < redhat-6.1.nsfocus.com.3389 > redhat-6.1.nsfocus.com.50001: S 2996115497:2996115497(0) win 31072 <mss 3884> (DF)
12:39:09.924941 lo > redhat-6.1.nsfocus.com.3389 > redhat-6.1.nsfocus.com.50001: S 2996115497:2996115497(0) win 31072 <mss 3884> (DF)
12:39:09.924941 lo < redhat-6.1.nsfocus.com.3389 > redhat-6.1.nsfocus.com.50001: S 2996115497:2996115497(0) win 31072 <mss 3884> (DF)
12:40:45.924939 lo > redhat-6.1.nsfocus.com.3389 > redhat-6.1.nsfocus.com.50001: S 2996115497:2996115497(0) win 31072 <mss 3884> (DF)
12:40:45.924939 lo < redhat-6.1.nsfocus.com.3389 > redhat-6.1.nsfocus.com.50001: S 2996115497:2996115497(0) win 31072 <mss 3884> (DF)
12:42:45.924938 lo > redhat-6.1.nsfocus.com.3389 > redhat-6.1.nsfocus.com.50001: S 2996115497:2996115497(0) win 31072 <mss 3884> (DF)
12:42:45.924938 lo < redhat-6.1. nsfocus.com.3389 > redhat-6.1.nsfocus.com.50001: S 2996115497:2996115497(0) win 31072 <mss 3884> (DF)
12:44:45.924940 lo > redhat-6.1.nsfocus.com.3389 > redhat-6.1.nsfocus.com.50001: S 2996115497:2996115497(0) win 31072 <mss 3884> (DF)
12:44:45.924940 lo < redhat-6.1.nsfocus.com.3389 > redhat-6.1.nsfocus.com.50001: S 2996115497:2996115497(0) win 31072 <mss 3884> (DF)
12:46:45.924941 lo > redhat-6.1.nsfocus.com.3389 > redhat-6.1.nsfocus.com.50001: S 2996115497:2996115497(0) win 31072 <mss 3884> (DF)
12:46:45.924941 lo < redhat-6.1.nsfocus.com.3389 > redhat-6.1.nsfocus.com.50001: S 2996115497:2996115497(0) win 31072 <mss 3884> (DF)
12:48:45.924939 lo > redhat-6.1.nsfocus.com.3389 > redhat-6.1.nsfocus.com.50001: S 2996115497:2996115497(0) win 31072 <mss 3884> (DF)
12:48:45.924939 lo < redhat-6.1.nsfocus.com.3389 > redhat-6.1.nsfocus.com.50001: S 2996115497:2996115497(0) win 31072 <mss 3884> (DF)
12:50:45.924942 lo > redhat-6.1.nsfocus.com.3389 > redhat-6.1.nsfocus.com.50001: S 2996115497:2996115497(0) win 31072 <mss 3884> (DF)
12:50:45.924942 lo < redhat-6.1.nsfocus.com.3389 > redhat-6.1.nsfocus.com.50001: S 2996115497:2996115497(0) win 31072 <mss 3884> (DF)

结论(请对照前面的代码):
0s 12:37:36.926670 //第一次发起连接

+3s 12:37:39.924937 //未收到回应,开始重试
+6s 12:37:45.924934 //在未超过TCP_RTO_MAX之前,每次重试时间翻倍
+12s 12:37:57.924934
+24s 12:38:21.924940
+48s 12:39:09.924941
+96s 12:40:45.924939

+120S 12:42:45.924938 //超过TCP_RTO_MAX之后,不再翻倍,而是固定用TCP_RTO_MAX,即2分钟
+120s 12:44:45.924940
+120S 12:46:45.924941
+120s 12:48:45.924939
+120s 12:50:45.924942

共用了大约13分钟零9秒,重试了11次(比设置的值多了一次,在maillist中发现有人提到了这个bug,并在新版本
中作了修正,见链接http://www.uwsg.indiana.edu/hypermail/linux/kernel/0001.0/0237.html)

重连次数linux不同版本也有变化:
在2.2.16的内核代码中(include/net/tcp.h):
#define TCP_SYN_RETRIES 10 /* number of times to retry opening a
* connection (TCP_RETR2-....) */
而在2.4.1中(include/net/tcp.h):
#define TCP_SYN_RETRIES 5 /* number of times to retry active opening a
* connection: ~180sec is RFC minumum */

我改了一下这个次数:
[root@redhat-6 yawl]# echo "5" > /proc/sys/net/ipv4/tcp_syn_retries
[root@redhat-6 yawl]# cat /proc/sys/net/ipv4/tcp_syn_retries
5
然后又测了一遍,结论类似,用时3分9秒。

其他系统的测试我没有时间做了,假如有有感爱好的测过了请把结果给我发一份yawl@docshow.net.

测试程序:
[yawl@redhat-6 tmp]$ cat connect.c
#include <sys/socket.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <stdio.h>
#include <time.h>

int main(int argc, char* argv[])
{
int fd;
struct sockaddr_in sa;
time_t cur;

if(argc!=3){
printf("%s IP PORT\n",argv[0]);
exit(-1);
}

fd=socket(AF_INET, SOCK_STREAM, 0);
if(fd<0){
perror("socket fail");
exit(-1);
}

sa.sin_family=AF_INET;
sa.sin_addr.s_addr=inet_addr(argv[1]);
sa.sin_port=htons(atoi(argv[2]));

cur=time(NULL);
printf("%s", ctime(&cur));
if(connect(fd, (struct sockaddr*)&sa, sizeof(sa)) < 0){
perror("connect fail");
}
cur=time(NULL);
printf("%s", ctime(&cur));

close(fd);
exit(0);
}

<eof>
视频教程列表
文章教程搜索
 
C语言程序设计推荐教程
C语言程序设计热门教程
看全部视频教程
购买方式/价格
购买视频教程: 咨询客服
tel:15972130058