TCP复习笔记(二) TCP服务器参见问题和参数设置

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选项

参考:http://tech.uc.cn/?p=1790

2. TIME_WAIT 状态

前面已经说过这个问题,解决这个问题的方法:

  • 尽量让客户端主动断开连接
  • 服务器监听套接字使用SO_REUSEADDR选项

3. 半打开连接

半打开连接(Half-Open)是指,一方已经关闭或异常终止连接而另一方还不知道。

比如客户端突然异常关机,没有发送FIN,而服务器并不知道客户端已经不存在,仍然维护着这个连接,占用着服务器资源。当客户端重启后,将使用一个新的临时端口,即通过一个新连接与服务器通信,而旧的半打开连接仍然存在(我们假设服务器不会主动向客户端发消息,如果有,参见服务器异常关闭的情况)。

而如果服务器异常关闭了,客户端仍然维护着这个连接,在服务器重启后,客户端尝试给服务器发消息,此时服务器将返回一个RST包,导致连接复位。

解决方法:

  • 应用层保活定时器:心跳机制
  • TCP层包括定时器:TCP的keepalive

4. 半关闭连接

半关闭连接是指一方结束了它的发送行为,但是还能够收到来自另一方的数据。即只关闭了一个方向上的通道。这可能是应用利用半关闭特性来做一些事情(尽管并不建议这么做),也可能是应用忘了关闭另一个方向上的通道。

通过上面的Shell命令统计出状态结果,其中状态FIN_WAIT2,即为半关闭状态。这通常也是服务器端需要注意的。