TCP核心参数
关于TCP核心参数,参见:https://www.frozentux.net/ipsysctl-tutorial/chunkyhtml/tcpvariables.html
其中比较重要的有:
tcp_syn_retries
三次握手中,发出的SYN未得到响应时,超时重传SYN包的次数
tcp_synack_retries
三次握手中最后一个ACK未收到时,超时重传SYN-ACK包的次数
tcp_max_syn_backlog
服务器端SYN队列大小,关于TCP Listener的状态转变,可参考下图:
tcp_abort_on_overflow
当服务器忙不过来时(listen backlog满了),发送RST包重置连接
tcp_tw_reuse
复用正在TIME_WAIT状态的端口
tcp_defer_accept
server端会在接收到最后一个ack之后,并不进入ESTABLISHED状态,而只是将这个socket标记为acked,然后丢掉这个ack。此时server端这个socket还是处于syn_recved,然后接下来就是等待client发送数据, 而由于这个socket还是处于syn_recved,因此此时就会被syn_ack定时器所控制。直到收到客户端第一个包(此时连接才ESTABLISHED)或重传超时(丢掉连接)。
针对客户端发送第一个包(典型地,如HTTP浏览器)的情况下,这个参数可以延迟连接的建立(ESTABLISHED),在应用层体现为延迟连接服务(进程/线程/Actor)的创建,对某些对最大连接(服务)数有限制的服务器,可以更充分地利用资源。并且由于少了服务的休眠/唤醒,可能在这方面有细微地性能提升。
下面是一些比较危险,通常不建议使用的选项:
tcp_syncookies
当SYN队列满时,可通过cookies的方式与客户端建立连接(即使该连接不在SYN队列中)。它违反了TCP三次握手协议,是非正规不严谨的。尽管对防止Syn Flood很有帮助
tcp_tw_recycle
打开TIME_WAIT状态套接字的快速回收,比tcp_tw_reuse更为激进,慎用
tcp_max_tw_buckets
处于TIME_WAIT状态的套接字最大数量,超过这个限制的套接字将被销毁
服务器常见问题
1. SYN Flood 攻击
SYN泛洪攻击是指伪造TCP请求,发送SYN包,被攻击服务器将该连接加入SYN队列中,发送SYN-ACK包,但永远等不到客户端的ACK包,直到超时重传SYN-ACK多次后,这种”半连接”才能正常释放。大量的这种请求会耗尽SYN队列,导致正常连接请求得不到响应。
通过Shell命令:
netstat -n | awk '/^tcp/ {++S[$NF]} END {for(a in S) print a, S[a]}'
查看当前所有连接的状态统计。SYN_RECV
状态即为服务器端等待客户端ACK的状态,当该状态的连接数量过多时,通常是遭受了SYN Flood攻击。
解决方法:
- 调整
tcp_synack_retries
,减少超时重发SYN-ACK的次数(默认为5次) - 调整
tcp_max_syn_backlog
,增大”半连接”队列,即SYN队列 - 不到万不得已不要使用
tcp_syncookies
选项
2. TIME_WAIT 状态
前面已经说过这个问题,解决这个问题的方法:
- 尽量让客户端主动断开连接
- 服务器监听套接字使用
SO_REUSEADDR
选项
3. 半打开连接
半打开连接(Half-Open
)是指,一方已经关闭或异常终止连接而另一方还不知道。
比如客户端突然异常关机,没有发送FIN,而服务器并不知道客户端已经不存在,仍然维护着这个连接,占用着服务器资源。当客户端重启后,将使用一个新的临时端口,即通过一个新连接与服务器通信,而旧的半打开连接仍然存在(我们假设服务器不会主动向客户端发消息,如果有,参见服务器异常关闭的情况)。
而如果服务器异常关闭了,客户端仍然维护着这个连接,在服务器重启后,客户端尝试给服务器发消息,此时服务器将返回一个RST包,导致连接复位。
解决方法:
- 应用层保活定时器:心跳机制
- TCP层包括定时器:TCP的keepalive
4. 半关闭连接
半关闭连接是指一方结束了它的发送行为,但是还能够收到来自另一方的数据。即只关闭了一个方向上的通道。这可能是应用利用半关闭特性来做一些事情(尽管并不建议这么做),也可能是应用忘了关闭另一个方向上的通道。
通过上面的Shell命令统计出状态结果,其中状态FIN_WAIT2,即为半关闭状态。这通常也是服务器端需要注意的。