控制参数 参数说明
tcp_keepcnt关闭一个非活跃连接之前进行探测的最大次数,默认为 8 次
tcp_keepidle对一个连接进行有效性探测之前运行的最大非活跃时间间隔,默认值为 14400(即 2 个小时)
tcp_keepintvl两个探测的时间间隔,默认值为 150 即 75 秒
我们来看一个具体的例子。在 testServer 端(AIX 主机)采用 tcp_keepidel=240(即 2 分钟):tcp_keepcnt=8:tcp_keepintvl=150(即 75 秒)的参数值;启动 testServer 上的 tcpdump 查看网络包的交互情况;从 testClient 端发起请求建立和 testServer 之间的一个 telnet 连接。在连接建立完成之后,拔出 testClient 端的网线并观察服务器端的数据输出(见清单 4)。
清单 4. telnet 连接在服务器端的 tcpdump 输出
1 # tcpdump -i en1 host testServer.cn.ibm.com
2 04:51:51.379716 IP testClient.cn.ibm.com.telnet.40621 》
testServer.cn.ibm.com.telnet: S 4097149880:4097149880(0)
3 04:51:51.379755 IP testServer.cn.ibm.com.telnet 》
testClient.cn.ibm.com.40621: S 2543529892:2543529892(0) ack 4097149881
4 04:51:51.380609 IP testClient.cn.ibm.com.telnet.40621 》
testServer.cn.ibm.com.telnet: 。 ack 1
5 。。。
6 04:51:54.924058 IP testServer.cn.ibm.com.telnet 》
testClient.cn.ibm.com.40621: P 676:696(20) ack 87
7 04:51:54.924909 IP testClient.cn.ibm.com.telnet.40621 》
testServer.cn.ibm.com.telnet: 。 ack 696
8 04:53:54.550192 IP testServer.cn.ibm.com.telnet 》
testClient.cn.ibm.com.40621: 。 695:696(1) ack 86
9 04:55:09.550997 IP testServer.cn.ibm.com.telnet 》
testClient.cn.ibm.com.40621: 。 695:696(1) ack 86
10 04:56:24.552053 IP testServer.cn.ibm.com.telnet 》
testClient.cn.ibm.com.40621: 。 695:696(1) ack 86
11 04:57:39.552615 IP testServer.cn.ibm.com.telnet 》
testClient.cn.ibm.com.40621: 。 695:696(1) ack 86
12 04:58:54.553446 IP testServer.cn.ibm.com.telnet 》
testClient.cn.ibm.com.40621: 。 695:696(1) ack 86
13 05:00:09.554287 IP testServer.cn.ibm.com.telnet 》
testClient.cn.ibm.com.40621: 。 695:696(1) ack 86
14 05:01:24.555117 IP testServer.cn.ibm.com.telnet 》
testClient.cn.ibm.com.40621: 。 695:696(1) ack 86
15 05:02:39.555958 IP testServer.cn.ibm.com.telnet 》
testClient.cn.ibm.com.40621: 。 695:696(1) ack 86
16 05:03:54.557282 IP testServer.cn.ibm.com.telnet 》
testClient.cn.ibm.com.40621: 。 695:696(1) ack 86
17 05:05:09.559795 IP testServer.cn.ibm.com.telnet 》
testClient.cn.ibm.com.40621: R 696:696(0) ack 87
从清单 4 中可以看出,第 6 行的报文是本连接发送的最后数据,而第 7 行则是对第 6 行数据的确认。其后,该连接上没有任何数据交互,从而使得该连接一直处于非活跃状态。经过 2 分钟(第 8 行数据报时间 04:53:54 和第 7 行数据报时间 04:51:54 之差,即 tcp_keepidle 的值)的非活跃时间后,第 8 行是服务器端发起第一个保活探测数据报。由于服务器端没有收到客户端关于探测报文的相应,因此再经过 tcp_keepintvl 的时间间隔(75 秒)之后,第 9 行显示服务器端再次发起保活探测数据报。服务器端持续发送了 tcp_keepcnt 个探测报文(上面结果显示,在 AIX 上是持续发送 tcp_keepcnt+1 个探测报文)之后,仍然没有收到来自客户端的任何回应,所以服务器在第 17 行向客户端发送复位报文同时在服务器端关闭了该连接。
需要注意的是,保活探测虽然通过发送 TCP 探测报文,但探测报文不会对正常的 TCP 连接产生任何影响。从清单 4 可以看出,第 8 行发送数据的 TCP 报文序号为 695 起始的 1Byte 数据,而该数据在第 6 行已经发送并被客户端确认。对于正常状态的连接,客户端在收到探测报文之后将返回一个第 7 行所示的 ACK 报文并借此向服务器端表明连接工作正常。
接下来,我们将通过一个实际的 TCP 断连的例子来分析上述机制对 TCP 连接保持的影响,并针对需要长时间保持 TCP 连接的应用提出两种可选的解决方案。
AIX 上的 TCP 断连及数据分析
图 3. 出现 TCP 断连的网络拓扑结构示意图
所有服务器主机均划为一个局域网,并处于防火墙 B 之后。由于工作需要,来自工作区局域网的主机 testClient 需和服务器局域网内的 testServer 上的数据库使用 TCP/IP 建立一个连接,testClient 上的上层应用将通过该连接对 testServer 上的数据库进行相应操作。
在实际测试中,我们发现,在 testClient 和 testServer 均工作正常的情况下,testClient 上的客户端在事先没有收到任何异常信息的情况下,所持有的连接会出现非预期的断连现象(在试图通过连接进行数据库操作时,会被告知 connection is reset by foreign host 的错误)。
由于该现象不断出现,并且网络内的中间节点(路由器和交换机等)均工作正常,因此可以排除物理因素(如掉电、宕机等)的可能。为了便于分析断连原因,我们首先查看了 testServer 机器上的默认保活设置:
# no -a | grep keep
tcp_keepcnt = 8
tcp_keepidle = 14400
tcp_keepintvl = 150
testServer 上的 tcp_keepidle 为 14400,即 2 个小时。既然中间节点工作正常,为什么保活机制没有其作用呢?为了进行分析,我们采用 tcpdump 工具捕获 testClient 和 testServer 上的报文信息,见清单 5 和清单 6 所示。
清单 5. 服务器端的 tcpdump 数据输出
1 10:18:58.881950 IP testClient.cn.ibm.com.59098 》
testServer.cn.ibm.com.telnet: S 1182666808:1182666808(0) 。。。
2 10:18:58.882001 IP testServer.cn.ibm.com.telnet 》
testClient.cn.ibm.com.59098: S 3333341833:3333341833(0) ack 1182666809 。。。
3 10:18:58.882845 IP testClient.cn.ibm.com.59098 》
testServer.cn.ibm.com.telnet: 。 ack 1 。。。
4 。。。
5 10:19:03.165568 IP testServer.cn.ibm.com.telnet 》
testClient.cn.ibm.com.59098: P 1010:1032(22) ack 87 。。。
6 10:19:03.166457 IP testClient.cn.ibm.com.59098 》
testServer.cn.ibm.com.telnet: 。 ack 1032 。。。
7 12:19:05.445336 IP testServer.cn.ibm.com.telnet 》
testClient.cn.ibm.com.59098: 。 1031:1032(1) ack 86 。。。
8 12:19:05.445464 IP testClient.cn.ibm.com.59098 》
testServer.cn.ibm.com.telnet: R 86:87(1) ack 1031 。。。
清单 6. 客户端的 tcpdump 数据输出
1 # tcpdump -e -i eth0 host testServer.cn.ibm.com
2 10:18:55.800553 IP testClient.cn.ibm.com.59098 》
testServer.cn.ibm.com.telnet: S 1182666808:1182666808(0) 。。。
3 10:18:55.801778 IP testServer.cn.ibm.com.telnet 》
testClient.cn.ibm.com.59098: S 3333341833:3333341833(0) ack 1182666809 。。。
4 10:18:55.801799 IP testClient.cn.ibm.com.59098 》
testServer.cn.ibm.com.telnet: 。 ack 1 。。。
5 。。。
6 10:19:00.084662 IP testServer.cn.ibm.com.telnet 》
testClient.cn.ibm.com.59098: P 1010:1032(22) ack 87 。。。
7 10:19:00.084678 IP testClient.cn.ibm.com.59098 》
testServer.cn.ibm.com.telnet: 。 ack 1032 。。。
从清单 5 中可以看出,在该连接处于非活跃状态的时间达到 tcp_keepidle 设定的 2 小时时,服务器主机发出了第一个连接保活的探测报文(清单 5 中的第 7 行)。紧接着,服务器主机就收到了来自 testClient 的连接复位报文(清单 5 中的第 8 行)。之后,服务器便关闭了该连接(可以通过 netstat –ni 来查看)。然而,从清单 6 的 tcpdump 数据可以看出, testClient 端并未发送任何报文。那么,是谁向 testServer 发送了复位报文呢?
为了查看上述复位报文的发送者,同样采用上述 tcpdump 命令再次捕获服务器端和防火墙 B 的报文信息(注意:通常需要捕获防火墙主机上网络数据的出口网卡和入口网卡数据),结果显示,防火墙 B 在收到来自 testServer 的第一个探测报文之后就立刻向 testServer 发送了一个复位报文。
上述分析说明,在连接传递完最后一个交互数据之后到服务器端发送第一个保活探测之间,该连接已经被防火墙 B 终止;在此之后,基于该连接的任何报文传递在试图穿过防火墙的时候均会被防火墙丢弃并发送复位报文。
两种常用的解决方案
针对上述 TCP 断连现象,有两种常用的解决方案可供选择:
方案 1、延长防火墙终止非活跃的 TCP 连接的时间。例如,针对上述案例,可以调节防火墙设置,将时间设置为大于服务器端设定的 2 小时。
方案 2、缩短服务器端的 TCP 连接保活时间。缩短该时间的目的是为了在连接被防火墙终止之前发送保活探测报文,既可以探测客户端状态,又可以使连接变为活跃状态。
对于第一种方案而言,延长 TCP 连接的保持时间可能会导致防火墙性能的降低,尤其是在维持大量长时间处于非活跃状态的连接的情况下更是如此;而对于第二种方案,如果缩短服务器端的 TCP 连接保活时间,意味着会增加网络中的数据报文数而占用额外的网络带宽。因此,两种方案各有利弊,需要依据不同的实际应用情况进行选择。
总结
本文介绍了 TCP 连接的建立和保持的相关概念以及影响 TCP 连接保持的常见因素。给出了常见的类 Unix 系统上 TCP 连接保活探测的相关配置参数,并基于 AIX 借助 tcpdump 工具分析了一个实际的 TCP 断连的案例。最后,针对 TCP 断连的情况给出了两种可行的解决方案。
评论
查看更多