TCP三次握手性能提升
因为TCP是面向连接的、可靠的、双向传输的传输层通信协议,所以在每次传输数据前都要进行三次握手才能建立连接。
可以说TCP三次握手的过程在一个HTTP请求中平均时间占比10%左右,当遇到网络不佳或者SYN攻击时,如果没有正确调节三次握手的参数,就会对TCP性能参数很大影响。
客户端优化
三次握手建立连接的首要目的是同步序列号
可靠传输是依靠同步的序列号实现的,比如流量控制,丢包重传等
其实SYN报文的全称就叫同步序列号
-
SYN_SENT状态优化
客户端发送完SYN报文后就处于SYN_SENT状态,等待服务器返回ACK+SYN报文,正常情况下服务器会迅速返回SYN+ACK报文。如果客户端长时间没有收到就会重发SYN包,重发此时由tcp_syn_retries参数控制,默认为5次
通常第一次重传为1秒后,后面每次重传时间都会加倍,直到第五次,发完第五次后,客户端会等待32秒,如果服务器依然没有返回ACK,则会终止本次握手。
总共持续时间1+2+4+8+16+32 = 63,大概1分钟
可以根据网络稳定性和服务器繁忙程度调整SYN重传次数。比如在内网通讯时,可以适当调低重传次数,尽快反应错误
服务器优化
服务器向客户端回复SYN和ACK报文后,就会进入SYN_RCV状态,这个状态下内核会建立一个半连接队列维护未完成握手的信息,当半连接对列溢出时,服务端就无法再建立新的连接。
SYN攻击就是攻击这个半连接队列,导致半连接队列溢出
Linux通过netstat -s就可以统计因为半连接队列溢出而丢弃的次数。得到的值是累加值,也就是说如果隔几秒执行这个命令,这个值有上升趋势,就说明半连接队列存在溢出现象。
增大SYN半连接队列大小
可以通过增大tcp_max_syn_backlog的值增大半连接队列大小,但是要注意的是,因为连接建立后半连接队列的连接会压入全连接队列,所以还需要一同增大sommaxconn和backlog的值,以增大全连接队列的大小。
增大accept全连接队列大小
accept队列的长度取决于somaxconn和backlog的最小值,即min{somaxconn,backlog}
-
somaxconn:默认值为128,Linux内核参数,可通过net.core.somaxconn设置它的值
-
backlog:是listen函数中backlog参数的大小
Tomact、Nginx、Apache常见的web服务器的backlog默认大小都是511
在accept状态下,通过ss -ltn命令可以查看accept队列的长度
Recv-Q:当前accpet队列的大小,表示放弃已经完成三次握手并等待服务端accept()TCP连接
Send-Q:accept队列最大的长度。
如果全连接队列发生溢出,可以通过netsat -s命令查看,因为accept队列溢出而被丢弃的连接次数。
想要在SYN半连接队列溢出时,连接不被丢弃,可以开启syncookies功能
syncookies的原理:服务器根据当前的状态计算出一个值,放在自己已经发出的SYN+ACK报文中,客户端接收到ACK报文时,取出来验证,如果合法则连接就已经建立成功了。
syncookies参数有三个值:
-
0值,表示已经关闭该功能
-
1值,表示当且仅当SYN半连接队列放不下时再开启它
-
2值,表示无条件一直开启该功能
在面对SYN攻击时,只需要将其值设为1即可
绕过三次握手
如果进行三次握手,就需要花费一个RTT的时间才能发送数据,所以要尽可能绕开三次握手
在Linux下可以开启TCP Fast Open功能减少TCP连接所需时间
大致过程
首次连接
-
客户端发送SYN报文,该报文包含Fast Open选项,且选项的Cookie为空,表明客户端请求Fast Open Cookies
-
支持TCP Fast Open的服务器收到SYN报文后,生成Cookie,Cookie跟随SYN-ACK数据包中的Fast Open选项发送给客户端
-
客户端收到SYN-ACK报文后,将Cookie进行缓存
再次连接
-
客户端将之前缓存的Cookie随SYN包一起发送给服务器
-
支持TCP Fast Open的服务器对收到的Cookie进行校验,如果Cookie有效,则发送SYN-ACK报文,告知Cookie有效,然后就准备发送数据。如果Cookie无效,服务器会丢弃SYN报文的数据,然后发送的SYN-ACK只对SYN报文的序列号进行确认
-
收到SYN-ACK包后,如果SYN包没有被接受,那么就重传SYN包的数据