TCP(Transmission Control Protocol)传输控制协议是一种面向连接的、可靠的、基于字节流的传输层协议。从TCP的定义中可以看出,TCP是面向连接的可靠的通信方式,连接双方都需要确定各自的连接通道都是OK的。三次握手的目的就是为了确定各自的通道是OK的,同时通知对方自己的起始SEQ。当客户端调用connect函数时便发送了SYN报文同时通知对方自己的起始SEQ,从而发起第一次握手,客户端进入SYN_SENT状态;服务端收到SYN报文后,返回SYN+ACK报文到客户端同时通知对方自己的起始SEQ,从而发起了第二次握手,服务端进入SYN_RCV状态;客户端收到服务端的SYN+ACK后,回复ACK报文,从而发起第三次握手,客户端进入ESTABLISHED状态;服务端收到ACK报文后服务端进入ESTABLISHED状态。自此三次握手就完成了,其流程图如下:

TCP的三次握手

过程如下:
image.png

客户端调优

1、当客户端发起第一次握手的时候,如果迟迟收不到对方的应答,那么客户端会重试,重试的次数由/proc/sys/net/ipv4/tcp_syn_retries控制,默认值是5,重试间隔是1,2,4,8,16,32,总共需要花费63s。可以根据客户端与服务端的网络状况以及服务端的负载,适当降低重试次数调小,如2次,从而出现异常的时候,客户端可以尽快感知到异常。

服务端调优

1、当服务端发起第二次握手的时候,如果迟迟收不到对方的应答,那么服务端也会重试,重试的次数由/proc/sys/net/ipv4/tcp_synack_retries控制,默认值是5,重试间隔是1,2,4,8,16,32,总共需要花费63s。同样的,如果客户端与服务器的网络状况挺好的,可以把重试次数调小。
2、当服务端发起第二次握手后,会把连接放入到半连接队列,队列的大小由/proc/sys/net/ipv4/tcp_max_syn_backlog控制,默认大小是2048。当这个队列满时,服务器将无法建立连接。那么我们怎么知道这个队列是否有溢出呢?可以通过如下的命令看到

netstat -s | grep 'SYNs to LISTEN'
75571 SYNs to LISTEN sockets ignored

这个是个累计值,如果发现这个值不断的变大,可以把这个队列调大。同时还可以设置/proc/sys/net/ipv4/tcp_syncookies=1,打开tcp_syncookies功能。tcp_syncookies可以取如下的值:
A. tcp_syncookies=0时,关闭syncookies功能,当半连接队列满时,服务端将无法建立连接;
B. tcp_syncookies=1时,当半连接队列满时,服务端将启用syncookies功能;
C. tcp_syncookies=2时,服务端将一直启用syncookies功能。
tcp_syncookies的主要工作流程如下:
服务端接收到SYN报文并返回TSYN+ACK报文时,不插入半连接队列,而是根据这个SYN报文计算出一个cookie值。这个cookie作为将要返回的SYN ACK报文的初始序列号。当客户端返回一个ACK报文时,根据报文头信息计算cookie,与返回的确认序列号(初始序列号+1)进行对比,如果相同,则是一个正常连接,然后将连接放入accept队列。
如果通过netstat -lap发现SYN_RCV状态的连接不断的增长,那么就需要通过抓包工具分析是不是发生了syn泛洪攻击,如果发生了泛洪攻击,需要通过防火墙阻止这种连接。
所谓的SYN泛洪攻击利用的是TCP的三次握手机制,攻击端利用伪造的IP地址向被攻击端发出请求,而被攻击端发出的响应报文将永远发送不到目的地,那么被攻击端在等待关闭这个连接的过程中消耗了资源,如果有大量的这种连接,主机资源将被耗尽,从而达到攻击的目的。
3、当服务端收到第三次握手的ACK应答报文后,服务端会把连接放入accept队列,accept队列由listen()函数传入的参数(参数的值可以通过ss -lnt命令的输出“Send-Q”列查看)与/proc/sys/net/core/somaxconn共同控制,取2者的小值。可以通过如下的命令查看是否由溢出:

netstat -s | grep overflow
75571 times the listen queue of a socket overflowed

如果这个值不断的变大,说明应用程序没有及时的调用accept函数接收连接,可以先调高上文提高的2个参数。
accept队列满后是否丢弃连接,是由参数/proc/sys/net/ipv4/tcp_abort_on_overflow控制,默认是0。如果设置为1并且溢出,则发送RST报文关闭连接。一般建议设置为0,因为丢弃后,客户端如果发送了数据+ACK,由于等不到应答,那么就会重复发送数据+ACK,当服务端接收到ACK后,如果accept队列不满了,那么连接就建立了。可见tcp_abort_on_overflow=0,可以提高TCP连接建立成功的概率。只有accept队列长期溢出的时候,才设置tcp_abort_on_overflow=1。
4、另外还可以打开TFO功能,提高TCP连接建立的效率,TFO详见文档:
https://tools.ietf.org/html/r...
https://www.cnblogs.com/passz...


aidanzheng
6 声望1 粉丝

毕业于西北大学数学系,12年软件开发经验的老程序员。10000小时定律是我的座右铭,不断的学习,不断突破自我


« 上一篇
TCP报文格式

引用和评论

0 条评论