问题现象:
访问一个我们新接入的业务接口,能ping通他们的站点,第一次telnet对端接口会通,第二次、第三次... 一直做下去就不通了,但是连接时超时服务器没有任何响应,怀疑问题出在了http的三次握手环节。
现象截图:
抓包的情况:
从抓包数据发现,出现问题的服务端的数据包来看 存在发出的tcpsyn包有时候响应,有时候不响应。不响应时,客户端与服务端之间的tcp连接无法正常建立,导致页面不能打开。从客户端抓包图里可以看到,发出去syn没有收到syn/ack,说明服务端就没有发ack,服务端是对端抓包,他们在网络入口也没有收到syn/ack,经过对比时间戳上有差异,发出的tcpsyn包带有时间戳,因此怀疑时间戳问题导致的故障
解决问题
既然怀疑是时间戳导致的,那我们就着手分析如果将服务端和客户端任意边把时间戳去掉会不会解决问题。针对带有时间戳的tcp syn包不响应的问题,查阅了相关资料得知产生问题的原因是出问题系统中的注册表中有Tcp1323opts这个选项,会导致其在发包时加入时间戳,经过NAT之后,如果前面相同的端口被使用过,且时间戳大于这个链接发出的syn中的时间戳,服务器上就会忽略掉这个syn,不返会syn-ack消息,表现为用户无法正常完成tcp3次握手,从而导致不能建立链接,链接超时。在业务闲时,如果用户nat的端口没有被使用过时,就可以正常打开;业务忙时,nat端口重复使用的频率高,很难分到没有被使用的端口,从而产生这种问题。
解决问题的方式有两种:
1、优化内核参数net.ipv4.tcp_timestamps,首先我们先查看一下我们服务器net.ipv4.tcp_timestamps的默认值,如果该值为0则说明不是该问题导致,如果是1我们需要将该值设置为0。
查看方法:
cat /proc/sys/net/ipv4/tcp_timestamps
解决办法:
echo "net.ipv4.tcp_timestamps = 0" >>/etc/sysctl.conf && sysctl -p
0表示:禁用时间戳
1表示:开启时间戳
TCP时间戳说明:
TCP时间戳选项会在TCP包头增加12个字节,以一种比重发超时更精确的方法来启用对RTT 的计算。
作用:
1、TCP时间戳位于TCP选项中,kind=8;lenth=10;data由timestamp和timestamp echo两个值组成,各4个字节的长度。
2、TCP时间戳理论作用有3个:序列号回绕,乱序的时间判断依据,避免确认二义性,以及计算RTT。
工作原理:
1、双方各自维护自己的时间戳,时间戳的值随时间单调递增(规定为1ms-1s/次,常见值为1ms、10ms)。
2、本端发送timesstamp值,对方收到后在后续的ACK的timestamp echo回应本端的值,并在timestamp中发送自己的时间戳。
3、TCP记录发送时间戳和收到回应的时间, 从而获得RTT。
2、修改客户端的注册表Tcp1323Opts设置为0。
说明:该参数控制 RFC 1323 时间戳与窗口缩放选项。默认情况下,启用时间戳与
窗口缩放,但是可以使用标志位进行控制。0 位控制窗口缩放,1 位控制时间戳。
值为0(禁用 RFC 1323 选项)
值为1(仅启用窗口缩放)
值为2(仅启用时间戳)
值为3(两个选项均启用)
什么情况下出现这个问题:
只有客户端和服务端都开启时间戳的情况下,才会出现能ping通不能建立tcp三次握手的情况,所以做为服务端提供服务的公司,不可能保证所有的用户都关闭时间戳,这个功能,所以我们必须关闭时间戳,这样才能给所用用户提供正常的服务。
通过这次报错分析分享大家几个不错的博客:
http://www.udpwork.com/item/6909.html
http://www.cnblogs.com/tolimit/p/5065761.html
http://www.cnblogs.com/lulu/p/4149312.html