TCP/IP 通信过程经常碰见的几个超时情况:
- 建连接时SYN超时
- SYN Flood攻击
- ISN的初始化
- MSL 和 TIME_WAIT
- TIME_WAIT数量太多以及解决方案
1、建连接时 SYN 超时
假设 server 端接到了clien 发的 SYN 后回了 SYN-ACK 后 client 掉线了,server 端没有收到 client 回来的 ACK,那么,这个连接处于一个中间状态,即没成功,也没失败。
于是,server 端如果在一定时间内没有收到的 TCP 会重发 SYN-ACK。在 Linux 下,默认重试次数为5次,重试的间隔时间从1s开始每次都翻售,5次的重试时间间隔为1s, 2s, 4s, 8s, 16s,总共31s,
第5次发出后还要等32s 都知道第5次也超时了,所以,总共需要 1s + 2s + 4s+ 8s+ 16s + 32s = 2^6 -1 = 63s,TCP才会把断开这个连接
2、SYN Flood 攻击
凡是有超时的地方就有 DDOS 攻击的可能。一些恶意的人就为此制造了SYN Flood 攻击——给服务器发了一个SYN 后,就下线了,于是服务器需要默认等63s才会断开连接,这样,攻击者就可以把服务器的syn连接的队列耗尽,让正常的连接请求不能处理。
于是,Linux下给了一个叫 tcp_syncookies 的参数来应对这个事——当 SYN 队列满了后,TCP 会通过源地址端口、目标地址端口和时间戳打造出一个特别的 Sequence Number 发回去(又叫cookie),如果是攻击者则不会有响应,如果是正常连接,则会把这个 SYN Cookie 发回来,然后服务端可以通过cookie 建连接(即使你不在 SYN 队列中)。
注意,请先千万别用 tcp_syncookies 来处理正常的大负载的连接的情况。因为,synccookies 是妥协版的 TCP 协议,并不严谨。对于正常的请求,你应该调整三个 TCP 参数可供你选择,
- 第一个是:tcp_synack_retries 可以用他来减少重试次数;
- 第二个是:tcp_max_syn_backlog,可以增大SYN连接数;
- 第三个是:tcp_abort_on_overflow 处理不过来干脆就直接拒绝连接了
3、ISN 的初始化
ISN 是不能硬编码,不然会出问题的——比如:如果连接建好后始终用1来做 ISN,如果 client 发了30个 segment 过去,但是网络断了,于是 client 重连,又用了1做 ISN,但是之前连接的那些包到了,于是就被当成了新连接的包,此时,client的Sequence Number 可能是3,而 Server 端认为 client 端的这个号是30了。全乱了。
RFC793 中说,ISN 会和一个假的时钟绑在一起,这个时钟会在每4微秒对 ISN 做加一操作,直到超过2^32,又从0开始。这样,一个ISN的周期大约是4.55个小时。
因为,我们假设我们的 TCP Segment 在网络上的存活时间不会超过 Maximum Segment Lifetime(缩写为MSL),所以,只要 MSL 的值小于4.55小时,那么,我们就不会重用到 ISN。
4、MSL 和 TIME_WAIT
通过上面的 ISN 的描述,相信你也知道 MSL 是怎么来的了。我们注意到,在 TCP 的状态图中,从 TIME_WAIT 状态到 CLOSED 状态,有一个超时设置,这个超时设置是 2*MSL(RFC793定义了MSL为2分钟,Linux设置成了30s)。
为什么要这有 TIME_WAIT,为什么不直接给转成 CLOSED 状态。主要有两个原因:
1)TIME_WAIT 确保有足够的时间让对端收到了 ACK,如果被动关闭的那方没有收到 Ack,就会触发被动端重发 Fin,一来一去正好2个MSL,
2)有足够的时间让这个连接不会跟后面的连接混在一起(你要知道,有些自做主张的路由器会缓存 IP数据包,如果连接被重用了,那么这些延迟收到的包就有可能会跟新连接混在一起)
5、TIME_WAIT数量太多以及解决方案
从上面的描述我们可以知道,TIME_WAIT 是个很重要的状态,但是如果在大并发的短链接下,TIME_WAIT 就会太多,这也会消耗很多系统资源。
网上很多教程会教你设置两个参数,一个叫 Tcp_tw_reuse,另一个叫 Tcp_tw_recycle 的参数,这两个参数默认值都是被关闭的,后者 recyle 比前者 resue 更为激进,resue 要温柔一些。
另外,如果使用 Tcp_tw_reuse,必需设置 Tcp_timestamps=1,否则无效。这里,你一定要注意,打开这两个参数会有比较大的坑——可能会让 TCP 连接出一些诡异的问题(因为如上述一样,如果不等待超时重用连接的话,新的连接可能会建不上。
tcp_tw_reuse:
官方文档上说 Tcp_tw_reuse 加上Tcp_timestamps(又叫PAWS, for Protection Against Wrapped Sequence Numbers)可以保证协议的角度上的安全,但是你需要 tcp_timestamps 在两边都被打开。
Tcp_tw_recycle:
如果是 tcp_tw_recycle 被打开了话,会假设对端开启了 tcp_timestamps,然后会去比较时间戳,如果时间戳变大了,就可以重用。
但是,如果对端是一个 NAT 网络的话(如:一个公司只用一个IP出公网)或是对端的 IP 被另一台重用了,这个事就复杂了。建链接的 SYN 可能就被直接丢掉了可能会看到 connection time out 的错误。
Tcp_max_tw_buckets:
这个是控制并发的 TIME_WAIT 的数量,默认值是180000,如果超限,那么,系统会把多的给 destory 掉,然后在日志里打一个警告(如:time wait bucket table overflow),官网文档说这个参数是用来对抗 DDoS 攻击的。也说的默认值180000并不小。这个还是需要根据实际情况考虑。
使用 Tcp_tw_reuse 和 Tcp_tw_recycle 来解决 TIME_WAIT 的问题是非常非常危险的,因为这两个参数违反了 TCP 协议
其实,TIME_WAIT 表示的是你主动断连接。如果让对端断连接,那么这个破问题就是对方的了。另外,如果你的服务器是于 HTTP 服务器,那么设置一个 HTTP 的 KeepAlive 有多重要(浏览器会重用一个 TCP 连接来处理多个 HTTP 请求),然后让客户端去断链接。
由于篇幅过长,TCP/IP 重传,控流,拥塞处理 将在下一篇文章,敬请关注~
本文来自投稿,不代表本人立场,如若转载,请注明出处:http://www.sosokankan.com/article/2047978.html