4.5 如何优化 TCP?
TCP 三次握手的性能提升
三次握手建立连接的首要目的是「同步序列号」。
只有同步了序列号才有可靠传输,TCP 许多特性都依赖于序列号实现,比如流量控制、丢包重传等,这也是三次握手中的报文称为 SYN 的原因,SYN 的全称就叫 Synchronize Sequence Numbers(同步序列号)。
客户端优化
SYN发送没有收到对应的ACK会重传,可以根据网络的稳定性和目标服务器的繁忙程度修改 SYN 的重传次数,调整客户端的三次握手时间上限。比如内网中通讯时,就可以适当调低重试次数,尽快把错误暴露给应用程序。
服务端优化
当服务端收到 SYN 包后,服务端会立马回复 SYN+ACK 包,表明确认收到了客户端的序列号,同时也把自己的序列号发给对方。
这个同样会触发重发,可以调节这个次数。
此时,服务端出现了新连接,状态是 SYN_RCV。在这个状态下,Linux 内核就会建立一个「半连接队列」来维护「未完成」的握手信息,当半连接队列溢出后,服务端就无法再建立新的连接。
SYN 攻击,攻击的是就是这个半连接队列。(可以增大这个队列)
如果使用 syncookies 可以不使用SYN半连接,可以通过这个避免SYN攻击。
accept队列可以设置为满了丢掉ACK包,可以提供连接成功率。
如何绕过三次握手?
通过 TCP Fast Open 功能,这个功能可以减少 TCP 连接建立的时延。
TCP Fast Open 功能需要客户端和服务端同时支持,才有效果。
TCP 四次挥手的性能提升
客户端和服务端双方都可以主动断开连接,通常先关闭连接的一方称为主动方,后关闭连接的一方称为被动方。
这里一点需要注意是:主动关闭连接的,才有 TIME_WAIT 状态。
主动关闭方和被动关闭方优化的思路也不同,接下来分别说说如何优化他们。
主动方的优化
主动发起 FIN 报文断开连接的一方,如果迟迟没收到对方的 ACK 回复,则会重传 FIN 报文,重传的次数由 tcp_orphan_retries 参数决定。
当主动方收到 ACK 报文后,连接就进入 FIN_WAIT2 状态,根据关闭的方式不同,优化的方式也不同:
- 如果这是 close 函数关闭的连接,那么它就是孤儿连接。如果
tcp_fin_timeout秒内没有收到对方的 FIN 报文,连接就直接关闭。同时,为了应对孤儿连接占用太多的资源,tcp_max_orphans定义了最大孤儿连接的数量,超过时连接就会直接释放。 - 反之是 shutdown 函数关闭的连接,则不受此参数限制;
当主动方接收到 FIN 报文,并返回 ACK 后,主动方的连接进入 TIME_WAIT 状态。这一状态会持续 1 分钟,为了防止 TIME_WAIT 状态占用太多的资源,tcp_max_tw_buckets 定义了最大数量,超过时连接也会直接释放。
当 TIME_WAIT 状态过多时,还可以通过设置 tcp_tw_reuse 和 tcp_timestamps 为 1,将 TIME_WAIT 状态的端口复用于作为客户端的新连接,注意该参数只适用于客户端。
被动方的优化
被动关闭的连接方应对非常简单,它在回复 ACK 后就进入了 CLOSE_WAIT 状态,等待进程调用 close 函数关闭连接。因此,出现大量 CLOSE_WAIT 状态的连接时,应当从应用程序中找问题。
当被动方发送 FIN 报文后,连接就进入 LAST_ACK 状态,在未等到 ACK 时,会在 tcp_orphan_retries 参数的控制下重发 FIN 报文。
TCP 传输数据的性能提升
TCP 连接是由内核维护的,内核会为每个连接建立内存缓冲区:
- 如果连接的内存配置过小,就无法充分使用网络带宽,TCP 传输效率就会降低;
- 如果连接的内存配置过大,很容易把服务器资源耗尽,这样就会导致新连接无法建立;
滑动窗口是如何影响传输速度的?
Linux 把 tcp_window_scaling 配置设为 1(默认打开),将滑动窗口大小设置为一个G,而不是两字节,64kb
通讯双方需要在各自的报文中发送这个选项
因为网络的传输能力是有限的,当发送方依据发送窗口,发送超过网络处理能力的报文时,路由器会直接丢弃这些报文。因此,缓冲区的内存并不是越大越好。
如何确定最大传输速度?
带宽时延积(BDP,Bandwidth Delay Product):它决定网络中飞行报文的大小,它的计算方式:

比如最大带宽是 100 MB/s,网络时延(RTT)是 10ms 时,意味着客户端到服务端的网络一共可以存放 100MB/s * 0.01s = 1MB 的字节。如果飞行报文超过了 1 MB,就会导致网络过载,容易丢包。
由于发送缓冲区大小决定了发送窗口的上限,而发送窗口又决定了「已发送未确认」的飞行报文的上限。因此,发送缓冲区不能超过「带宽时延积」。
发送缓冲区与带宽时延积的关系:
- 如果发送缓冲区「超过」带宽时延积,超出的部分就没办法有效的网络传输,同时导致网络过载,容易丢包;
- 如果发送缓冲区「小于」带宽时延积,就不能很好的发挥出网络的传输效率。
所以,发送缓冲区的大小最好是往带宽时延积靠近。
发送缓冲区是自行调节的,当发送方发送的数据被确认后,并且没有新的数据要发送,就会把发送缓冲区的内存释放掉。
接收缓冲区可以根据系统空闲内存的大小来调节接收窗口:(发送缓冲区的自动调节默认开启,接受缓冲区的需要手动配置)
