首页 > 编程语言 > TCP性能调优实现原理及过程解析
2020
11-29

TCP性能调优实现原理及过程解析

三次握手阶段

客户端SYN包的重试次数

sysctl -w net.ipv4.tcp_syn_retries=6

相关介绍

第 1 次重试发生在 1 秒钟后,接着会以翻倍的方式在第 2、4、8、16、32 秒共做 6 次重试,最后一次重试会等待 64 秒,如果仍然没有返回 ACK,才会终止三次握手。所以,总耗时是 1+2+4+8+16+32+64=127 秒,超过 2 分钟。

服务端半连接池大小

sysctl -w net.ipv4.tcp_max_syn_backlog=16384

服务端半连接池满了以后是否开启syncookie机制

sysctl -w net.ipv4.tcp_syncookies=1

相关介绍

如果 SYN 半连接队列已满,默认会丢弃连接并不是这样,开启 syncookies 功能就可以在不使用 SYN 队列的情况下成功建立连接。

syncookies 是这么做的:服务器根据当前状态计算出一个值,放在己方发出的 SYN+ACK 报文中发出,当客户端返回 ACK 报文时,取出该值验证,如果合法,就认为连接建立成功,如下图所示。

  • 0 表示关闭该功能;
  • 2 表示无条件开启功能;
  • 1 则表示仅当 SYN 半连接队列放不下时,再启用它。

注意:由于 syncookie 仅用于应对 SYN 泛洪攻击(攻击者恶意构造大量的 SYN 报文发送给服务器,造成 SYN 半连接队列溢出,导致正常客户端的连接无法建立),这种方式建立的连接,许多 TCP 特性都无法使用。所以,应当把 tcp_syncookies 设置为 1,仅在队列满时再启用。

服务端SYN+ACK包的重试次数

net.ipv4.tcp_synack_retries=5

相关介绍

tcp_synack_retries 的默认重试次数是 5 次,与客户端重发 SYN 类似,它的重试会经历 1、2、4、8、16 秒,最后一次重试后等待 32 秒,若仍然没有收到 ACK,才会关闭连接,故共需要等待 63 秒。

服务端全连接队列的大小

取决于min(backlog, /proc/sys/net/core/somaxconn),在linux内核2.2版本以后,listen 函数的 backlog 参数就可以设置 accept 队列的大小。

另外backlog 参数还受限于 Linux 系统级的队列长度上限,当然这个上限阈值也可以通过 somaxconn 参数修改,somaxconn是内核的参数,默认是128。

sysctl -w net.core.somaxconn=32768

四次挥手阶段

接下来我们把先关闭连接的一方叫做主动方,后关闭连接的一方叫做被动方。

四次挥手的流程:

其实四次挥手只涉及两种报文:FIN 和 ACK。FIN 就是 Finish 结束连接的意思,谁发出 FIN 报文,就表示它将不再发送任何数据,关闭这一方向的传输通道。ACK 是 Acknowledge 确认的意思,它用来通知对方:你方的发送通道已经关闭。当主动方关闭连接时,会发送 FIN 报文,此时主动方的连接状态由 ESTABLISHED 变为 FIN_WAIT1。当被动方收到 FIN 报文后,内核自动回复 ACK 报文,连接状态由 ESTABLISHED 变为 CLOSE_WAIT,顾名思义,它在等待进程调用 close 函数关闭连接。当主动方接收到这个 ACK 报文后,连接状态由 FIN_WAIT1 变为 FIN_WAIT2,主动方的发送通道就关闭了。再来看被动方的发送通道是如何关闭的。当被动方进入 CLOSE_WAIT 状态时,进程的 read 函数会返回 0,这样开发人员就会有针对性地调用 close 函数,进而触发内核发送 FIN 报文,此时被动方连接的状态变为 LAST_ACK。当主动方收到这个 FIN 报文时,内核会自动回复 ACK,同时连接的状态由 FIN_WAIT2 变为 TIME_WAIT,Linux 系统下大约 1 分钟后 TIME_WAIT 状态的连接才会彻底关闭。而被动方收到 ACK 报文后,连接就会关闭。

主动方的优化

等待ACK,FIN包的重发次数

主动方发送 FIN 报文后,连接就处于 FIN_WAIT1 状态下,该状态通常应在数十毫秒内转为 FIN_WAIT2。只有迟迟收不到对方返回的 ACK 时,才能用 netstat 命令观察到 FIN_WAIT1 状态。此时,内核会定时重发 FIN 报文,其中重发次数由 tcp_orphan_retries 参数控制(注意,orphan 虽然是孤儿的意思,该参数却不只对孤儿连接有效,事实上,它对所有 FIN_WAIT1 状态下的连接都有效),默认值是 0,特指 8 次:

net.ipv4.tcp_orphan_retries = 0

孤儿连接的数量

net.ipv4.tcp_max_orphans = 16384

相关介绍

tcp_max_orphans 定义了孤儿连接的最大数量。当进程调用 close 函数关闭连接后,该连接是在 FIN_WAIT1 状态,这个连接都与该进程无关了,它变成了孤儿连接。Linux 系统为防止孤儿连接过多,导致系统资源长期被占用,就提供了 tcp_max_orphans 参数。如果孤儿连接数量大于它,新增的孤儿连接将不再走四次挥手,而是直接发送 RST 复位报文强制关闭。

孤儿连接的定义:由进程调用close关闭的连接称为孤儿连接,另外shutdown 函数也可以关闭连接,这二者都会向对方发送 FIN 报文(shutdown 参数须传入 SHUT_WR 或者 SHUT_RDWR 才会发送 FIN),区别在于 close 调用后,哪怕对方在半关闭状态下发送的数据到达主动方,进程也无法接收。如果你用 netstat -p 命令,会发现连接对应的进程名为空(与进程无关!)。而 shutdown 函数调用后,即使连接进入了 FIN_WAIT1 或者 FIN_WAIT2 状态,它也不是孤儿连接,进程仍然可以继续接收数据。

等待FIN的时间

net.ipv4.tcp_fin_timeout = 60

相关介绍

当连接收到 ACK 进入 FIN_WAIT2 状态后,就表示主动方的发送通道已经关闭,接下来将等待对方发送 FIN 报文,关闭对方的发送通道。这时,如果连接是用 shutdown 函数关闭的,连接可以一直处于 FIN_WAIT2 状态。但对于 close 函数关闭的孤儿连接,这个状态不可以持续太久,而 tcp_fin_timeout 控制了这个状态下连接的持续时长。

TIME_WAIT相关参数

相关介绍

TIME_WAIT 是主动方四次挥手的最后一个状态。当收到被动方发来的 FIN 报文时,主动方回复 ACK,表示确认对方的发送通道已经关闭,连接随之进入 TIME_WAIT 状态,等待 60 秒后关闭。

TIME_WAIT状态最大连接数

当 TIME_WAIT 的连接数量超过该参数时,新关闭的连接就不再经历 TIME_WAIT 而直接关闭。

net.ipv4.tcp_max_tw_buckets = 5000

是否复用TIME_WAIT状态的端口

复用 TIME_WAIT 状态的端口,如果服务器会主动向上游服务器发起连接的话,就可以把 tcp_tw_reuse 参数设置为 1,它允许作为客户端的新连接,在安全条件下使用 TIME_WAIT 状态下的端口。

net.ipv4.tcp_tw_reuse = 1

当然,要想使 tcp_tw_reuse 生效,还得把 timestamps 参数设置为 1,它满足安全复用的先决条件(对方也要打开 tcp_timestamps ):

net.ipv4.tcp_timestamps = 1

老版本的 Linux 还提供了 tcp_tw_recycle 参数,它并不要求 TIME_WAIT 状态存在 60 秒,很容易导致数据错乱,不建议设置为 1。

net.ipv4.tcp_tw_recycle = 0

所以在 Linux 4.12 版本后,直接取消了这一参数。

其他配置

允许系统打开的端口范围

sysctl -w net.ipv4.ip_local_port_range=1024 65000

系统全局允许分配的最大文件句柄数

sysctl -w fs.file-max=2097152

sysctl -w fs.nr_open=2097152

echo 2097152 > /proc/sys/fs/nr_open

允许当前会话或进程打开文件句柄数

ulimit -n 1048576

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持自学编程网。

编程技巧