-
概述
-
五层模型
- 在网络的五层模型中,由下至上分别为:实体层,链接层,网络层,传输层,应用层.
- 实体层传输0,1构成的电信号
- 链接层 该层有PPP(点对点协议),Ethernet(以太网)等协议
- Ethernet 协议规定一组电信号构成一个数据包,叫做帧(Frame),每一帧分为head和data,head里面包含了发送方mac地址,接收方mac地址等内容
- head
- 固定18字节
- data
- 最小长度46字节
- 最大长度1500字节
- 长度称为MTU(Maximum Transmission Unit,最大传输单元)典型值为1500字节.
- 如果帧长度大于1518字节,需要分成多个帧进行传输,
- head
- Ethernet 协议规定一组电信号构成一个数据包,叫做帧(Frame),每一帧分为head和data,head里面包含了发送方mac地址,接收方mac地址等内容
- 网络层使用IP协议 包括ipv4协议和ipv6协议
- IP协议的数据包叫包(Packet),每个包分为head和data,head里面包含了发送方ip地址,接受方IP地址等内容,如果一个包的长度大于MTU,就需要分成多个帧发送.
- ipv4 head
- 最小长度 20字节
- 最大长度 60字节
- 典型值 20字节
- ipv4包最大长度 65,535字节
- ipv6 head
- 固定40字节
- 可通过扩展头部扩展
- 传输层经常使用tcp协议和udp协议,tcp协议是面向连接的,udp协议是面向无连接的
- tcp协议的数据包叫做段(Segment),每个段分为head和data,head里面包含发送方端口和接收方端口等内容,
- head
- 最小长度 20字节
- 最大长度 60字节
- 典型值 20字节
- data
- 长度称为MSS,MSS=MTU(Maximum Segment Size)-IP头长度-TCP头长度,典型值为1460
- head
- udp协议的数据包叫做数据报(Datagram),每个数据报分为head和data,head里面包含发送方端口和接收方端口等内容
- head
- 固定为8字节
- 总长度不超过65535字节
- head
- tcp协议的数据包叫做段(Segment),每个段分为head和data,head里面包含发送方端口和接收方端口等内容,
- 应用层协议也分为head和data两部分组成。放在udp或tcp的数据部分,如http,smtp,ftp等
- 在网络的五层模型中,由下至上分别为:实体层,链接层,网络层,传输层,应用层.
-
tcp协议
- tcp协议的特点相对传输层的另一个协议udp,有如下特点
- 可靠性
- 提供可靠的服务。通过序列号、确认应答、重传机制、以及数据完整性检验等技术,确保数据正确且按序到达接收方。
- 连接性
- tcp是面向连接的协议。在数据传输前,必须建立连接(三次握手过程)。数据传输完成后,需要断开连接(四次挥手过程)。
- 传输效率
- 由于需要建立连接、确认数据、进行流量控制和拥塞控制等,其头部开销较大(最小20字节),并且在网络条件不佳时重传机制可能导致更高的延迟。
- 可靠性
- 对于需要实时性和不需要很高的可靠性的应用场景,一般避免使用tcp,如流媒体,游戏等
- tcp是全双工协议,全双工与半双工,单工通信相对
- 全双工通信允许数据在两个方向上同时传输
- 半双工通信允许数据在两个方向上交替传输,但不可同时进行
- 单工通信则只允许数据在一个方向上传输,没有反向交互的可能。
- tcp协议的特点相对传输层的另一个协议udp,有如下特点
-
-
建立连接
-
三次握手
- tcp使用三次握手来建立一个连接,在典型场景如下:
-
- 客户端发送一个SYN包(SYN=1, seq=x)到服务端
-
- 服务端发送一个SYN+ACK包(SYN=1, ACK=1, seq=y, ACK=x+1)到客户端
-
- 客户端发送一个ACK包(ACK=1, seq=x+1, ACK=y+1)到服务端
-
- 此时服务端被称为被动打开(passive open),客户端被称为主动打开(active open)
- 服务端和客户端监听伪代码如下:
-
# server int sockfd = socket(AF_INET, SOCK_STREAM, 0); bind(sockfd, (struct sockaddr*)&server_addr, sizeof(server_addr)); # backlog参数指定了socket可以排队的最大连接数。具体来说,它定义了 # 在处理接受连接之前,等待队列中可以挂起的连接请求的最大数目。 # 这个数目包括了正在进行三次握手的半开连接,以及已经完成握手, # 等待程序通过accept()调用接受的完全建立的连接。 listen(sockfd, backlog); int client_sockfd = accept(sockfd, NULL, NULL); #client int sockfd = socket(AF_INET, SOCK_STREAM, 0); connect(sockfd, (struct sockaddr*)&server_addr, sizeof(server_addr));
- tcp使用三次握手来建立一个连接,在典型场景如下:
-
TCB(Transmission Control Block)
- 每当一个 TCP 连接被建立时,系统就会为该连接创建一个 TCB,它包含了管理和维护该连接所需的全部信息,tcb结构的定义包括连接使用的源端口、目的端口、目的ip、序号、应答序号、对方窗口大小、己方窗口大小、tcp状态、tcp输入/输出队列、应用层输出队列、tcp的重传有关变量等。
-
四元组
- TCP 四元组是用来唯一标识网络中的一个 TCP 连接的。包括源IP地址,源端口,目的ip地址,目的端口
- 服务器端接受连接的数量是无限的,只受内存的限制,客户端connect时将端口设置为0,操作系统会自动选择一个端口,在linux4.2之前,客户端在connect时需要分配一个空闲端口,限制了客户端对外发出连接的数量上限,linux4.2之后,使用socket选项IP_BIND_ADDRESS_NO_PORT,该选项告知内核在端口号为0时不保留临时端口。只要四元组是唯一的,就可以以这种方式共享源端口,同一个客户端端口可用于连接不同的服务器套接字
-
服务端等待队列
- 服务器端socket执行了listen函数后,会在socket建立两个队列:
- SYN队列:存放完成了二次握手的结果。 队列长度由listen函数的参数backlog指定。
- ACCEPT队列:存放完成了三次握手的结果。队列长度由listen函数的参数backlog指定。
-
相关参数
- backlog的默认值受两个内核参数控制,somaxconn和tcp_max_syn_backlog
- 服务器端socket执行了listen函数后,会在socket建立两个队列:
-
发送缓冲区和接受缓冲区
- 客户端和服务端都有一个socket套接字,双方都可以通过各自的套接字进行发送和接收消息,socket里面维护了两个缓冲区,一个发送缓冲区,一个接收缓冲区。
-
发送缓冲区
- 发送缓冲区则用于存储待发送的数据。当应用程序调用send()或write()时,数据首先被放入用户空间,然后复制到内核的发送缓冲区中。这个过程确保了数据按照最大报文段长度(MSS)进行分片,并在确认收到ACK之前不会被移除。如果发送缓冲区满了,新的发送请求可能会被阻塞,直到有足够的空间可用
- 发送缓冲区如图所示
-
接收缓冲区
- 接收缓冲区用于存储从网络接收到的数据包。每当服务器的网卡接收到一个TCP数据包时,该数据包会被复制到内核缓冲区,并最终存放在接收缓冲区中。接收缓冲区的大小取决于应用程序的处理能力和配置参数。如果接收缓冲区满了,而应用程序又没有及时读取数据,这可能导致数据包丢失,并且通过流控通知发送端停止发送
- 接收缓冲区如图所示
- 如接收到了1-50,51-100,201-250 3个段,那么连续读取段的最后一个位置是100,接受到的段的最后一个位置是250
-
相关参数
- tcp_rmem和tcp_wmem
-
为什么需要三次握手
- 目的是为了防止已经失效的握手请求发送到服务端
- 假设client发出握手请求a,请求a在某个网络节点中长时间滞留,以致延迟到该连接已经被client释放(如client的connect方法设置了200ms的超时时间,请求a的滞留时间超过200ms)才到达server
- 假设只有两步握手,server向client发送一个ACK,连接建立,server等待client发送数据,但是client并没有建立新连接,会忽略server的确认,server会一直等待client发送数据,直到超时,造成资源浪费.
- 采用三次握手,server向client发送一个SYN-ACK,然后等待client回应ACK,因为client不会回应ACK,server就会知道client并没有要求建立连接,
- 假设client发出握手请求a,请求a在某个网络节点中长时间滞留,以致延迟到该连接已经被client释放(如client的connect方法设置了200ms的超时时间,请求a的滞留时间超过200ms)才到达server
- 目的是为了防止已经失效的握手请求发送到服务端
-
未收到第三步的ACK,会发生什么
- 如果服务端回复SYN-ACK后,客户端掉线,没有收到客户端的ACK,服务端会在指数回退时间下进行重试,在重试多次仍然未收到ACK后,会将该连接移除SYN队列
-
TFO(TCP Fast Open)
- TCP Fast Open (TFO) 是一种旨在加速 TCP 连接建立的协议扩展,TCP 连接需要进行三次握手方可开始传输数据。这使得每个 TCP 连接都需要浪费掉一个 RTT 去建立连接。
-
开启过程
-
请求Fast Open Cookie
- 客户端发送SYN数据包,该数据包包含Fast Open选项,且该选项的Cookie为空,这表明客户端请求Fast Open Cookie
- 支持TCP Fast Open的服务器生成Cookie,并将其置于SYN-ACK数据包中的Fast Open选项以发回客户端
- 客户端收到SYN-ACK后,缓存Fast Open选项中的Cookie。
-
使用Fast Open Cookie
- 客户端发送SYN数据包,该数据包包含数据,且该数据包的Fast Open选项包含之前缓存的Cookie
- 服务端收到Cookie进行校验,如果Cookie有效,服务器将在SYN-ACK数据包中对SYN和数据进行确认,确认中也可以携带数据,,如果无效,服务端将丢弃数据包中包含的数据,仅ACK对应的序列号
- 客户端将发送ACK确认服务器发回的SYN以及数据
-
-
对比
- 由图可知客户端可以节省1.5个RTT,服务端可以节省1个RTT
-
存在的问题
- 由于这是一项对 TCP 协议的扩展,中间网络设备有可能不支持该特性。比方说有些防火墙(NAT)会直接将带有数据段的 SYN 包认为是非法数据包直接抛弃。导致使用了 TFO 的连接完全无法建立。(如中国的移动数据网络)
- 为此,操作系统不得不引入一个 blackhole 机制,当对某 IP 以 TFO 进行握手时,如果在一定时间内都没有收到回应,那么就重新尝试以非 TFO 方式进行握手,如果成功,则将该 IP 加入黑名单,之后不再以 TFO 进行握手。
- 但该机制有两个问题:
- 可能因为网络正常波动丢包而误判,导致 IP 进入黑名单,后续连接全部丧失 TFO 特性。
- 有的时候是在特定网络下 TFO 无效(如数据网络),但操作系统的黑名单只记录了目标 IP,所以在切换网络后,依然无法使用 TFO。
-
-
数据传输
-
消息确认
-
Seq(Sequence Number)和ACK(Acknowledgement)
-
ACK 机制
- 每一个发送到对端的段都会包含一个序列号,对端的ACK响应中会包含下一个期望接收的字节的序列号
- 例如,如果一个段的序列号是 1,数据长度是 10,那么接收方发送的 ACK 会包含序列号 11,意味着它已经成功接收到序号 1 到 10 的数据,并期待收到序号为 11 的数据。
- ACK指示到目前为止连续接收到的最后一个字节。如果中间有数据丢失,即便后面的数据已经接收到,发送方还是需要重传丢失的数据之后的所有数据。
- 每传输一个字节,序列号会递增1
- 接收端接收到多个段时,即使乱序,也可以根据序列号进行重组
- ACK并不意味着已经ACK的数据已经交付到了上层应用
- 每一个发送到对端的段都会包含一个序列号,对端的ACK响应中会包含下一个期望接收的字节的序列号
-
ISN(Initial Sequence Number)
- 当两个 TCP 端点建立连接时,它们各自随机生成一个 ISN,序号是32bits的无符号数,在它增大到2^32-1时,便会回绕到0
-
-
SACK(Selective Acknowledgment)
- SACK在RFC 2018 中定义,SACK可以提高在网络条件不理想时的数据传输效率,允许接收方确认它成功收到不连续的块,以及基础TCP确认的成功收到最后连续字节序号
- SACK位于Options选项中
-
举例
- 假设发送方发送了序列号为 1-10、11-20、21-30、31-40 和 41-50 的五个数据包。接收方成功接收了序列号为 1-10、21-30 和 41-50 的数据包,但序列号为 11-20 和 31-40 的数据包丢失了。
- 在传统的 TCP 中,接收方只能发送 ACK 11(表示期待接收序列号为 11 的数据包),发送方需要重新发送序列号为 11 之后所有的数据包。
- 使用 SACK 时,接收方可以发送一个 ACK 11,但同时在 SACK 选项中指示已经接收到序列号为 21-30 和 41-50 的数据段。发送方收到这个信息后,只需要重传序列号为 11-20 和 31-40 的数据包。
-
为什么需要SACK
- 如果中间有数据丢失,即便后面的数据已经接收到,没有SACK机制时,需要重传丢失的数据之后的所有数据,在SACK机制下发送方可以只重传那些未被确认接收的数据块。
-
开启过程
- 在 Linux下,可以通过tcp_sack参数打开SACK功能(Linux 2.4后默认打开)
- SACK 的开启过程在 TCP 的三次握手中完成:
- SYN 包:在建立连接的初始阶段,客户端发送一个带有 SYN 标志的 TCP 段。在这个段的 TCP 头部选项字段中,客户端可以包含一个 SACK选项,表明它支持 SACK 机制。
- SYN-ACK 包:服务器在收到客户端的 SYN 请求后,如果也支持 SACK,则在返回的 SYN-ACK 响应中同样包含 SACK选项,确认双方都支持 SACK 功能。
- ACK 包:客户端发送确认包(ACK)给服务器,完成三次握手。此时,如果双方都在 SYN 和 SYN-ACK 包中声明了对 SACK 的支持,那么 SACK 机制即被启用。
-
D-SACK(Duplicate Selective Acknowledgment)
- D-SACK是SACK的一个扩展,定义在RFC 2883,用来通知发送方收到了哪些重复数据包
-
如何区分SACK和D-SACK
- 如果SACK的第一个段的范围被ACK所覆盖,那么就是D-SACK;
- 如果SACK的第一个段的范围被SACK的第二个段覆盖,那么就是D-SACK
-
举例
- ACK丢包
sender-> 1-1000 ->reciever -> ACK 1001(丢失) ->sendersender-> 1001-2000 ->reciever-> ACK 2001(丢失) ->sendersender-> 1-1000(超时重传) ->reciever-> ACK 2001,SACK 1-1000 ->sender
- reordering
- 如果接收到的数据段不是按照顺序的,即存在一个跳跃(例如,期望的是序号为 51 的数据段,但收到了序号为 101 的数据段),接收方会重复发送最后一个连续接收的数据段的 ACK(51)。发送方收到这样的重复 ACK 可能会认为在此之后的数据段已丢失。
sender-> 1-50 ->reciever -> ACK 51 ->sendersender-> 51-100(延时) ->recieversender-> 101-150 ->reciever -> ACK 51,SACK 101-150 ->sendersender-> 151-200 ->reciever -> ACK 51,SACK 101-200 ->sendersender-> 201-250 ->reciever -> ACK 51,SACK 101-250 ->sender触发快速重传 sender-> 51-100 ->reciever -> ACK 251 ->sender延时收到 sender-> 51-100 ->reciever -> ACK 251,SACK 51-100 ->sender
- ACK丢包
-
为什么需要D-SACK
- TCP 发送方可能会将乱序到达的数据段误解为丢失的数据段。如果发生这种情况,TCP 发送方将重传乱序数据包之前的数据段,并减慢该连接的数据传输速率。通过引入了 D-SACK 选项,发送方知道其初始的丢包判断是错误的,并且可以安全地恢复到之前较高的数据传输速率,而不需要维持基于误判的低速率传输
- D-SACK告诉发送端两种可能情况:
-
- 收到了重复的数据,数据包没有丢,丢的是ACK包
-
- 快速重传算法触发了重传,既不是发出去的包丢了,也不是因为回应的ACK包丢了,而是因为网络延时导致的reordering(网络上出现了先发的包后到的情况)。
-
-
-
消息重传
-
超时重传
- 当发送方在规定的时间内没有收到接收方对已发送数据段的ACK确认时,发送方会认为该数据段丢失,并进行重传。
- 每当发送方收到确认包后,会重置这个重传定时器,如果重传定时器被触发,仍然没有收到确认包,定时器的值将被设为前次值的二倍(直到特定阈值)
- 定时器的值设定为
smoothed RTT+max(G,4*RTT variation), G为时钟粒度,RTT variation为RTT的偏差
-
Fast Retransmit(快速重传)
- 当发送方收到三个连续的重复ACK时,它将立即重传未被确认的数据段,而不需要等待数据段的重传计时器超时。
- 发送方实际需要收到4次重复ACK,第一次ACK为正常的响应ACK,后三次为重复ACK
-
为什么需要快速重传
- 在超时重传的情况下,如果一个数据段丢失,可能需要等待较长时间(例如,几秒钟)才能重传。快速重传机制允许在较短的时间内(例如,几毫秒)重传丢失的数据段,从而减少延迟并提高数据传输的效率。
-
举例
- 假设发送方发送了序列号为 1-10、11-20、21-30、31-40,41-50 共五个数据段。接收方成功接收了序列号为 1-10,21-30,31-40,41-50的数据段,11-20的数据在网络中丢失,接收方在分别收到21-30,31-40,41-50的数据,会重复发送ACK 11,发送方收到三个连续的重复ACK 11,会立即重传11-20的数据
-
SACK
- 在上方的例子中,如果没有SACK,发送方不知道应该重传11-20,还是后续所有的数据,使用SACK后,发送方知道只有11-20的数据丢失,只重传11-20的数据
-
区别
- 超时重传表明可能存在广泛的网络问题,如严重拥塞或不稳定的网络路径。
- 快速重传则指示网络中可能只有局部的问题,网络大部分区域仍然可以正常工作。超时重传相对于快速重传严重的多
-
-
RTT(Round Trip Time)和RTO(Retransmission Time Out)
- RTT: 往返时间,表示一个数据包从发送端到接收端,再返回发送端的时间
- RTO: 重传超时时间,在重传数据包之前等待ACK的时间
- 如果RTO过小,可能导致数据包被误判为丢失,需要频繁重传,导致网络拥塞
- 如果RTO过大,重发会慢,效率低
- 因为网络状况是动态变化的,所以RTT和RTO也需要动态变化
-
经典算法:指数加权移动平均(Exponential weighted moving average)
- 定义在RFC793中,计算过程如下:
-
公式
-
- 记录最近几次的RTT值
-
- 计算平滑RTT(SRTT):
SRTT = α * SRTT + (1 - α) * RTT其中α是平滑因子,通常取值为0.8或0.9
- 计算平滑RTT(SRTT):
-
- 计算RTO:
RTO = min(ubound, max(lbound, β * SRTT))其中β是放大因子,通常取值为1.3到2.0之间,lbound和ubound是RTO的下限和上限
- 计算RTO:
-
-
举例
- 假设上次SRTT为150ms,最新RTT为200ms
- 计算SRTT:
SRTT = 0.8 * 150ms + 0.2 * 200ms = 160ms - 计算RTO:
RTO = min(ubound, max(lbound, β * SRTT))- 假设ubound为300ms,lbound为100ms,β为1.5
RTO = min(300ms, max(100ms, 1.5 * 160ms)) = 240ms
-
存在的问题
- 如果发生了重传, 如何计算RTT
- 如果第一次ACK丢失,使用t3-t1,偏大
- 如果第一次ACK是滞留,使用t3-t2,偏小
-
Karn/Partridge 算法
- 为了解决经典算法存在的问题,Karn/Partridge 算法被提出,最大特点是忽略重传,不采样重传的RTT,如上述中,会采用t3-t1作为RTT
-
存在的问题
- 在正常网络下RTO较小,假设为150ms,如果网络波动,发生较大的延时,导致多个数据包被重传,这几个包的RTT较大如800ms,因为重传的数据包的RTT不被采样,所以RTO不会增加,会导致恶性循环,为解决该问题该算法会直接对RTO进行翻倍,导致RTO没有那么准确
-
Jacobson/Karels 算法
- 定义在RFC6289中,是当前TCP协议使用的算法,最大特点是使用一个单独的变量DevRTT计算平滑RTT和RTT的偏差,并使用该变量计算RTO,避免了RTT突然有了一个大幅度波动时,被加权移动平均算法拉低
-
公式
- 计算平滑RTT(SRTT):
SRTT = SRTT + α (RTT – SRTT) - 计算RTT偏差(DevRTT):
DevRTT = (1 - β) * DevRTT + β * |SRTT - RTT| - 计算RTO:
RTO = µ * SRTT + ∂ *DevRTT - 一般取值 α=0.125,β=0.25,µ=1,∂=4,各项因子都是经验值
- 计算平滑RTT(SRTT):
-
举例
- 假设上次SRTT为150ms,最新RTT为200ms,上次DevRTT为50ms
- 计算SRTT:
SRTT = 150ms + 0.125 * (200ms - 150ms) = 156.25ms - 计算DevRTT:
DevRTT = (1 - 0.25) * 50ms + 0.25 * |156.25ms - 200ms| = 37.5ms + 11.25ms = 48.75ms - 计算RTO:
RTO = 1 * 156.25ms + 4 * 48.75ms = 348.75ms
-
流量控制
- 流量控制用来控制发送方发送数据的速度,使得接收方有足够的时间处理接收到的数据
- "窗口"是指发送方可以发送但未被ACK的数据量,窗口大小是动态变化的由接收方告知发送方,通过上述接收和发送缓冲区里面的参数可以得到窗口大小,窗口大小 = 接受缓冲区最大值-接受到的段的最后一个位置-1
- 每当发送方收到一个ACK,它就会将窗口向前滑动,即将已ack的数据包从窗口中移除,并可以发送新的数据包。
-
滑动窗口
-
接收窗口
-
发送窗口
-
ZWP(Zero Window Probe 零窗口探针)
- 当接收方处理数据缓慢,最后导致接收窗口为0,接收方会在ack中告知发送方接收窗口为0,发送方会停止发送数据,并开启一个定时器(persist timer), 避免因随后的修改接收窗口的数据包丢失使连接的双侧进入死锁,当定时器到期时,发送方会发送一个ZWP包,期待接收方回复一个带着新的接收窗口大小的ack包,一般重试3次,如果仍然为0,发送方一般会发送RST,断开连接
-
Silly window syndrome(糊涂窗口综合症)
- 如果接收方在接收窗口满后每次都只能处理一小部分数据,它会ack一系列小的窗口。这被称作愚蠢窗口综合症,因为它在TCP的数据包中发送很少的一些字节,相对于TCP包头是很大的开销。解决这个问题,就要避免对小的window size做出响应,直到有足够大的window size再响应
- MSS的典型值为1460字节,如果每次只发送一小部分数据,比如几个字节,相当于一辆客车只运送几个乘客,效率很低,所以需要避免这种情况
-
解决方案
- 接收端使用David D Clark算法:如果收到数据后导致窗口大小小于某个值,直接ack关闭窗口,阻止发送方继续发送数据,等到下面两个条件满足一个
-
- 窗口大小>=MSS
-
- 接收缓冲区一半为空
-
- 发送端使用Nagle算法:下面两个条件满足一个
-
- 窗口大小 >= MSS 并且 待发送数据长度 >= MSS
-
- 等待时间超过200ms
-
- 接收端使用David D Clark算法:如果收到数据后导致窗口大小小于某个值,直接ack关闭窗口,阻止发送方继续发送数据,等到下面两个条件满足一个
-
-
拥塞控制
-
为什么需要拥塞控制
- 拥塞控制是发送方根据网络的承载情况控制分组的发送量,以获取高性能又能避免拥塞崩溃
-
拥塞崩溃(congestion collapse):
- 当网络中的数据包因为过度拥塞而大量丢失,导致网络有效吞吐量急剧下降,甚至接近于零。
- 丢包导致大量的数据需要重传,但重传本身又进一步加剧了网络拥塞
-
- 如果TCP发送方都是以恒定的速率发送数据,当带宽资源有限时,网络中会出现大量的排队等待处理的数据包,导致网络性能下降,甚至崩溃,所以需要对发送流量进行控制
- 如果在数据传输过程中发生丢包,这通常意味着网络拥塞。TCP将根据丢包的情况来大幅降低拥塞窗口的大小,并重新设置慢启动阈值,然后重新开始慢启动过程。
- 拥塞控制是发送方根据网络的承载情况控制分组的发送量,以获取高性能又能避免拥塞崩溃
-
拥塞窗口(cwnd,Congestion Window)
- 拥塞窗口主要用于控制因网络拥塞而可能引起的数据包丢失
- 简单讲,如果所有分段到达接收方和确认包准时地回到发送方,拥塞窗口会增加一定数量。如果发生超时和丢包,则会减少
-
和滑动窗口的区别
- 滑动窗口的大小由接收方根据其当前的处理能力和缓冲区大小动态设置,而拥塞窗口的大小由发送方根据网络的拥塞情况动态调整
- 滑动窗口避免接收方被发送方的数据淹没,拥塞窗口避免网络拥塞
- 实际的传输能力由拥塞窗口和滑动窗口共同决定,取两者的较小值
-
TCP Tahoe 和TCP Reno
- TCP Tahoe 引入了3个基本的机制,分别是慢启动,拥塞避免,快速重传
- TCP Reno是TCP Tahoe 的改进版, 在Tahoe的基础上增加了快速恢复(Fast Recovery)算法,并且改进了快速重传阶段窗口变化,减少了拥塞窗口的剧烈下降
-
慢启动
- 慢启动是指TCP连接开始时,发送方逐渐增加发送数据量,以观察网络的拥塞情况,避免一开始就发送大量数据导致网络拥塞
- 当TCP连接开始时,拥塞窗口初始化为一个较小的值,通常是2个MSS的大小
- 每当收到一个ACK,拥塞窗口就增加一个MSS的大小,这意味着每个往返时间(RTT)内,拥塞窗口大小都会翻倍。这种增长方式是指数的
- 即 收到ACK时,cwnd = cwnd + 1, 每过一个RTT,cwnd = cwnd * 2
- 例如:
- 初始时,cwnd = 1 MSS
- 第一个RTT后,收到1个ACK,cwnd 变为 2 MSS
- 第二个RTT后,收到2个ACK,cwnd 变为 4 MSS
- 第三个RTT后,收到4个ACK,cwnd 变为 8 MSS
- 以此类推,cwnd 在每个RTT后都会翻倍
- 为了防止拥塞窗口无限制地增长,TCP还设置了一个慢启动阈值(ssthresh)。通常情况下慢启动阈值的初始值为65535字节,当拥塞窗口的大小增加到这个阈值时,TCP将从慢启动模式切换到拥塞避免模式。
-
拥塞避免(Congestion Avoidance)
- 当拥塞窗口的大小增加ssthresh时,TCP将从慢启动模式切换到拥塞避免模式。
- 在拥塞避免模式下,cwnd会线性增长
- 收到ACK时,cwnd = cwnd + 1/cwnd, 每过一个RTT,cwnd = cwnd + 1
-
快速重传
- 在接收到三个重复ACK时立即重传丢失的数据包
- TCP Tahoe算法中快速重传后,拥塞窗口改变和RTO超时一样如下
- sshthresh = cwnd /2, 且sshthresh需要大于等于 2 MSS,cwnd = 1
- 重新进入慢启动过程
- TCP RENO
- cwnd = cwnd /2
- sshthresh = cwnd
-
快速恢复
- TCP Reno快速恢复算法是在快速重传之后执行的。此算法的目的是减少因为数据包丢失而可能引起的拥塞窗口的剧烈下降,从而避免TCP连接进入慢启动阶段。
- 在收到快速重传的3个重复ACK后时,在快速重传阶段,cwnd和sshthresh已更新为
- cwnd = cwnd /2
- sshthresh = cwnd
- 然后快速恢复算法定义如下:
- cwnd = ssthresh + 3 * MSS(3倍MSS对应于收到的三个重复ACK)
- 收到重复ACK时,cwnd = cwnd + 1
- 收到新的ACK时,cwnd = ssthrethresh,然后进入拥塞避免
- 例如:
- 假设一个 TCP 连接的 cwnd = 12 MSS,ssthresh = 24 MSS。突然之间,发送方收到了三个针对同一个数据包的重复 ACKs。
- 检测到丢包,触发快速重传,不等待超时。
- 进入快速恢复,ssthresh 和cwnd都被设置为当前 cwnd 的一半,即 12 MSS / 2 = 6 MSS。cwnd 调整为 ssthresh + 3 = 6 + 3 = 9 MSS。
- 重传丢失的数据包。
- 假设在快速恢复期间收到了2个额外的重复 ACK,每收到一个重复 ACK,cwnd 增加1 MSS,因此 cwnd 从9增加到11 MSS。
- 最后,当发送方收到一个新的 ACK 时,这表明丢失的数据包和后续的一些数据包已经被成功接收。此时,将 cwnd 重置为 ssthresh 的值,即 6 MSS,并结束快速恢复,继续拥塞避免。
-
拥塞窗口变化图
-
TCP BIC , CUBIC,BBR
- TCP BIC(Binary Increase Congestion control)和 TCP CUBIC 是两种较新的TCP拥塞控制算法,主要用于高带宽和高延迟的网络环境(例如宽带互联网连接),这种网络环境通常被称为长肥网络(Long Fat Networks, LFNs)。这两种算法都是为了解决传统TCP拥塞控制算法在这些网络环境下性能不足的问题
-
长肥网络
- 这类网络通常在带宽资源充足但信号传输时间较长的场景中出现,例如跨洲或卫星通信。在这种类型的网络中,数据可以以高速率传输,但数据从发送端到接收端的总传输时间(往返时间,RTT)相对较长。
- 大带宽*延迟积(Bandwidth-Delay Product, BDP) BDP 是网络带宽和往返时间的乘积,用来估算在单个往返时间内能够“在途”的最大数据量。在长肥网络中,这个值通常很大,意味着网络能够在任何时候容纳大量未确认的数据
-
传统TCP拥塞控制的问题
- 传统的TCP拥塞控制机制(如TCP Reno)在长肥网络中表现不佳,这些算法通常依赖于丢包作为网络拥塞的信号,因为在高延迟环境中,响应丢包的速度可能不够快,导致带宽未被充分利用。
- 假设一个跨大西洋的光纤连接,其带宽非常高(例如10 Gbps),而RTT可能高达100ms或更多。在这种情况下,如果使用TCP Reno,网络中的数据包丢失会导致整个窗口大小减半,并且慢启动重新开始。由于高延迟,恢复到最大窗口大小需要更多时间,期间网络带宽被严重浪费。
- 传统的TCP拥塞控制机制(如TCP Reno)在长肥网络中表现不佳,这些算法通常依赖于丢包作为网络拥塞的信号,因为在高延迟环境中,响应丢包的速度可能不够快,导致带宽未被充分利用。
-
TCP Reno的问题
- 由于传统算法在遇到丢包时会将拥塞窗口剧烈减半,这导致带宽利用率在遭遇网络波动后不能迅速恢复到最优状态,从而无法充分利用可用的带宽,这种算法的窗口增长策略在逐渐接近网络容量时也过于保守,增长速度慢,导致带宽未被充分利用。
-
-
TCP BIC
- 核心思想是在窗口增长阶段使用二分查找的方法来快速逼近最佳的窗口大小。具体来说,TCP BIC在检测到网络拥塞(通常是通过丢包发现)并减小窗口后,会在后续的恢复阶段采用二分法迅速调整窗口大小,以便尽快找到当前网络状态下的最佳窗口值。
-
流程
- 窗口减少:当检测到丢包时,TCP BIC 将当前窗口设置为最大窗口值(Wmax),并且将当前窗口值降低β倍(0.2),作为最小窗口值(Wmin)
- 二分增长:在减小之后,TCP BIC不是简单地线性增加窗口,而是在Wmax和Wmin之间使用一种二分查找策略(快速逼近最佳窗口大小)。这种方法可以使窗口在发生拥塞后更快地恢复,特别适用于大带宽*延迟积(BDP)的网络。
- 稳定期:当窗口大小接近网络容量时,TCP BIC会细微调整其窗口增长速度,以避免过度波动和进一步的丢包。
-
对比TCP Reno
- 假设带宽: 1 Gbps
- 往返时间(RTT): 100 ms
- 初始拥塞窗口大小: 1000 个数据包
-
Reno
- 丢包发生时,窗口大小: 1000 包
- 立即将窗口减半: 500 包
- 进入拥塞避免阶段,每个RTT增加1个MSS(最大报文段长度,假设为1460字节)
- 恢复到原来的窗口大小需要的时间:
- (1000 - 500) RTT = 500
- 500 * 100ms = 50秒
-
BIC
- 丢包发生时,窗口大小: 1000 包 (Wmax)
- 将窗口减小到Wmin: 1000 * 0.8 = 800 包
- 使用二分搜索快速探测:
- 第1个RTT: 尝试 (1000 + 800) / 2 = 900 包
- 第2个RTT: 如果成功,尝试 (1000 + 900) / 2 = 950 包
- 第3个RTT: 如果成功,尝试 (1000 + 950) / 2 = 975 包
- 第4个RTT: 如果成功,尝试 (1000 + 975) / 2 = 987 包
- 假设在第4次RTT后找到合适的窗口大小 总恢复时间: 4 RTT = 4 * 100ms = 0.4秒
-
TCP BIC的缺点
- 快速收敛到Wmax在长RTT的情况下,是比较适合的,但是在短RTT的劣网环境下,数据包往返速度快,窗口增长也更快。激进的增窗会引发带宽的争抢,使得拥塞控制不具备公平性
- 更激进的算法可能会获得不成比例的高带宽份额,违背了TCP拥塞控制的公平性原则。
-
TCP CUBIC
- TCP CUBIC实际上是一种改进的BIC。BIC在短RTT下增窗快,本质是因为其算法依赖RTT。CUBIC的实现方式整体和BIC一样,只不过增窗不再依赖RTT, 是一种基于丢包的拥塞控制算法,它使用一个三次函数来增加和减少拥塞窗口的大小。Cubic 在 Linux 内核 2.6.19 之后被广泛使用,不会在短RTT场景下再有快速增窗的问题了
-
BBR
- TCP BBRv3 是由 Google 开发的最新一代拥塞控制算法,它是 BBR 算法的第三个版本。BBRv3 的核心思想是通过测量网络的带宽和往返传播时间(RTT)来控制数据的发送速率,而不是依赖于丢包等传统的拥塞信号。
- BBRv3 使用一个基于模型的方法来动态探测和模拟网络路径,主要关注两个参数:瓶颈带宽(网络路径中的最大带宽)和最小RTT
- BBRv3 使用带宽、RTT、ECN(显式拥塞通知)和丢包等信号来调整发送速率。
- TCP BIC(Binary Increase Congestion control)和 TCP CUBIC 是两种较新的TCP拥塞控制算法,主要用于高带宽和高延迟的网络环境(例如宽带互联网连接),这种网络环境通常被称为长肥网络(Long Fat Networks, LFNs)。这两种算法都是为了解决传统TCP拥塞控制算法在这些网络环境下性能不足的问题
-
-
-
断开连接
- 终止连接使用4次挥手,连接的每一端都可以独立的终止,当一端想要终止时,向对侧发送FIN,对侧回复ACK,因此完整的连接终止,需要一对FIN/ACK,分别由两侧端点发出
- 连接可以工作在TCP半开状态。即一侧关闭了连接,不再发送数据;但另一侧没有关闭连接,仍可以发送数据。已关闭的一侧仍然应接收数据,直至对侧也关闭了连接。
- 断开过程状态变化所示
-
TIME_WAIT的作用
- 确保连接可靠关闭
- 在TCP连接的终止阶段,双方需要交换FIN和ACK。TIME_WAIT状态确保了最后一个ACK消息被对方成功接收。如果这个ACK消息在网络中丢失,对方将重发FIN消息。处于TIME_WAIT状态的端点仍然可以接收并处理这个重发的FIN消息,并再次发送ACK,确保连接可靠地关闭。
- 确保老的数据包不会被新连接接收
- 网络中可能仍然存在延迟的数据包,这些数据包属于刚刚关闭的连接。TIME_WAIT状态持续的时间(通常是MSL的两倍,约为2分钟)足以让这些老的数据包在网络中“死亡”,避免它们被错误地交付到新的同一端口和IP地址组合的连接中。
- MSL(Maximum Segment Lifetime) 是指数据段在网络中的最大生存时间,RFC793定义了MSL为2分钟,Linux设置成了30s,参数tcp_max_tw_buckets控制并发的TIME_WAIT的数量,详解见下方参数汇总
- 例如
- 客户端 A 与服务器 S 建立了一个 TCP 连接,使用端口对 (IP_A:1234, IP_S:80)。
- 这个连接传输完数据后开始关闭过程。客户端 A 发送 FIN 包,进入 FIN_WAIT_1 状态。
- 服务器 S 回应 ACK,进入 CLOSE_WAIT 状态
- 服务器 S 发送 FIN 包,进入 LAST_ACK 状态。
- 客户端 A 回应最后的 ACK,进入 TIME_WAIT 状态。
- 如果没有TIME_WAIT,客户端 A 又立即用相同的端口 1234 与服务器 S 的 80 端口建立新连接。
- 新连接可能会立即使用相同的端口对 (IP_A:1234, IP_S:80)
- 如果网络中还存在旧连接的延迟数据包(比如因为网络拥塞或路由变化导致的延迟包),这些包可能会被错误地传递到新连接中
- 新连接可能会收到不属于它的数据,导致数据混乱或安全问题
- 如果有了TIME_WAIT,客户端 A 在TIME_WAIT状态结束前无法使用相同的端口 1234 与服务器 S 的 80 端口建立新连接
- 确保连接可靠关闭
-
断开代码如下
-
close(conn); # 把引用计数减1,如果引用计数为0,则开始关闭流程,且会关闭读写两个方向 shutdown(conn,SHUT_WR|SHUT_RD|SHUT_RDWR); # 立即关闭连接,发送FIN包,关闭参数指定方向的连接,SHUT_WR只关闭写方向,SHUT_RD只关闭读方向,SHUT_RDWR关闭读写方向
-
-
异常断开的情况
- 以服务端举例
-
服务端进程崩溃
- 进程崩溃后,服务端操作系统会向客户端发送一个FIN,如果客户端TCP仍然发送数据,服务端发送RST中断连接
-
服务器主机崩溃或断电
- 客户端不会立即察觉到,重发几次数据后会遇到ETIMEOUT错误。
-
服务端不可达
- 客户端不会立即察觉到,重发几次数据后会遇到EHOSTUNREACH错误。
-
其他问题
-
TCP"粘包"
- "粘包"指的是如客户端发送了n次小的数据包,TCP将其合并到了一个TCP段中发送,这就是粘包,"粘包"是应用层协议需要解决的问题
- TCP 是一种流协议,没有消息边界,只保证数据可以可靠的,按序到达.
- 解决办法:
- 消息定界符
- 每个消息都有固定长度
- 消息起始位置增加固定的长度字段,用来表示消息长度
- 序列化数据,如json协议
- http协议是如何处理粘包的
- 1.协议格式和内容长度指示
- HTTP协议有明确的格式,包括请求行/状态行、头部字段和消息体。头部与消息体之间有空行分隔,这种结构化的格式也有助于区分不同的消息。
- HTTP响应头中包含 Content-Length 字段,明确指出消息体的长度。例如:
-
HTTP/1.1 200 OK Content-Type: text/plain Content-Length: 23 This is the message body - 接收方可以根据 Content-Length 的值准确知道应该读取多少字节的数据。
-
- 分块传输编码:
- 允许服务器在不知道整个响应内容大小的情况下开始发送响应。这种方式特别适用于响应体的大小未知或动态生成的内容,如大文件的传输或实时数据流。
- 每个数据块都以其大小(以十六进制表示)开始,后跟一个回车换行(CRLF),然后是数据本身和另一个 CRLF。传输结束时,发送一个大小为0的块,表示没有更多的数据块。
- 分块传输编码的响应不能使用
Content-Length头,因为响应的总长度是未知的。 - 分块传输在 HTTP/2 中被废弃,因为 HTTP/2 采用了不同的机制来处理相同的问题,即帧的概念。
-
HTTP/1.1 200 OK Content-Type: text/plain Transfer-Encoding: chunked \r\n\r\n 7\r\n Hello, \r\n 6\r\n World!\r\n 0\r\n \r\n \r\n\r\n表明消息头结束7\r\n:表明接下来的块大小是7字节("Hello, " 的长度)。Hello, \r\n:块内容,紧跟一个 CRLF。6\r\n:表明下一个块的大小是6字节("World!" 的长度)。World!\r\n:第二块的内容,同样跟一个 CRLF。0\r\n:表明后面没有更多的块,块大小为0。\r\n:最后一个 CRLF 作为结束标志。
- 1.协议格式和内容长度指示
-
-
数据包结构
-
0 1 2 3 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | Source Port | Destination Port | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | Sequence Number | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | Acknowledgment Number | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | Data | |C|E|U|A|P|R|S|F| | | Offset| Rsrvd |W|C|R|C|S|S|Y|I| Window | | | |R|E|G|K|H|T|N|N| | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | Checksum | Urgent Pointer | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | [Options] | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | : : Data : : | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+- SourcePort: 16 bits
- 源端口
- Destination Port: 16 bits
- 目标端口
- Sequence Number: 32 bits
- 序列号
- Acknowledgment Number: 32 bits
- 确认号
- Data Offset: 4 bits
- 表示 TCP头部的长度,数据偏移字段的单位是32位字(4字节)。例如,如果数据偏移字段的值为5,那么TCP头部的长度就是5 × 4 = 20字节。
-
计算方式
```golang tcpStart := ipv4HeaderLength dataOffset := int(packet[tcpStart+12] >> 4) // First 4 bits tcpHeaderLength := dataOffset * 4 ```
- Reserved: 4 bits
- 保留位:必须为0
- RFC 9293 对之前的RFC 793的定义进行了修改,RFC 793中Reserved占用6bits,Control Bits 占用 6 bits,没有CWR和ECE
- Control Bits: 8 bits
- CWR: Congestion Window Reduced,拥塞窗口减小,为1表示发生了网络拥塞,通知对方主机已经采取措施减少发送窗口的大小,以减缓网络拥塞。
- ECE: ECN(ECN-Echo)通常和CWR一起使用。ECN允许网络设备(如路由器)在遇到即将发生的拥塞时,通过修改IP和TCP头部中的ECN字段来通知发送方和接收方,而不是丢弃包。当发送方接收到ECE标志的通知时,它会启动拥塞控制机制,减少拥塞窗口,并在下一个合适的TCP段中设置CWR标志,以告知接收方它已经减少了发送速率。
- URG:紧急指针字段,为1表示高优先级数据包
- ACK:确认字段,1表示确认号字段有效
- PSH:推送功能,为1表示是带有PUSH标志的数据,指示接收方应该尽快将这个报文段交给应用层而不用等待缓冲区装满。
- RST:重置连接,为1表示出现严重差错。可能需要重新建立TCP连接。还可以用于拒绝非法的报文段和拒绝连接请求。
- SYN:同步序列号,为1表示这是连接请求或是连接接受请求,用于建立连接和使顺序号同步
- FIN:为1表示发送方没有数据要传输了,要求释放连接。
- Window: 16 bits
- 接收窗口大小
- Checksum: 16 bits
- 校验和
-
func CheckSum(data []byte) uint16 { var ( sum uint32 length int = len(data) index int ) for length > 1 { sum += uint32(data[index])<<8 + uint32(data[index+1]) index += 2 length -= 2 } if length > 0 { sum += uint32(data[index]) } sum += sum >> 16 return uint16(^sum) }
- Urgent Pointer: 16 bits
- 紧急标志位(URG:
- TCP头部的控制位中有一个叫做URG的位,当这个位被设置时,表示该TCP段包含紧急数据。紧急指针字段此时才被视为有效,并用来指示紧急数据的范围。
- 计算紧急数据的范围:
- 紧急指针的值加上序列号(该段TCP段的起始序列号)给出了紧急数据的结束位置的序列号。例如,如果序列号是1000,紧急指针是500,那么紧急数据的范围是从序列号1000到1499。
- 示例
- 假设有一个TCP段,其序列号为1000,URG标志位被设置,紧急指针值为300。这意味着从序列号1000开始的300字节是紧急数据,紧急数据的范围是1000到1299。接收方的TCP堆栈会特殊处理这300字节的数据,可能会通过不同的机制(如不同的队列或事件)将其传递给应用程序。
- 紧急标志位(URG:
- Options: variable
- 由于TCP头部的数据偏移字段最多只能表示60字节的头部长度,所以所有选项的总长度必须在40字节以内(因为基本的TCP头部长度是20字节)。
- 每个选项的开始是1字节的kind字段,说明选项的类型。
- 常见的TCP选项
- 最大报文段长度(MSS): 32 bits
- 最大报文段长度(4字节,Maximum Segment Size,MSS)通常在建立连接而设置SYN标志的数据包中指明这个选项,指明本端所能接收的最大长度的报文段。通常将MSS设置为(MTU-40)字节,携带TCP报文段的IP数据报的长度就不会超过MTU,从而避免本机发生IP分片。只能出现在同步报文段中,否则将被忽略。
- 窗口比例(Window Scale):24 bits
- 取值0-14。用来把TCP的窗口的值左移的位数,使窗口值乘倍。只能出现在同步报文段中,否则将被忽略。这是因为现在的TCP接收数据缓冲区(接收窗口)的长度通常大于65535字节。对于高速或长距离的网络连接特别有用。
- 时间戳(Timestamps):80bits
- 用于计算往返时间(RTT)和防止序列号的环绕(当序列号用尽并重新开始时)。
- 发送端的时间戳: 32 bits, 时间戳回显应答: 32 bits
- 选择性确认(SACK):
- SACK选项的长度取决于包含的SACK块的数量。通常,一个TCP段可以包含多个SACK块。每增加一个SACK块,长度增加8字节。例如:
- 一个SACK块的情况:
- Kind: 1字节
- Length: 1字节
- SACK Block: 8字节
- 总计:1 + 1 + 8 = 10字节
- 两个SACK块的情况:
- Kind: 1字节
- Length: 1字节
- SACK Blocks: 8字节 * 2 = 16字节
- 总计:1 + 1 + 16 = 18字节
- 允许接收方指示已经接收到的数据块,而不是仅仅确认最后一个连续的字节
- 无操作(No-Operation,NOP):8 bits
- 用于对齐选项,使得后续的选项位于32位的边界上。
- 结束列表(End of Option List,EOL):8 bits
- 用于指示选项列表的结束,通常用于填充,以确保TCP头部的总长度是32位字的整数倍。
- 最大报文段长度(MSS): 32 bits
- Padding: variable
- 由于TCP头部的长度必须是32位字的倍数,填充用于确保这一点。这是因为TCP的数据偏移字段定义了头部的长度,而这个长度包括了基本头部和选项的总和。
- SourcePort: 16 bits
-
-
相关参数
- somaxconn :
- 指定系统全局级别上,每个监听socket的最大backlog值。如果你在listen()函数中指定的backlog值超过了somaxconn的设定,那么实际的backlog值将被限制为somaxconn的值。
sysctl net.core.somaxconn net.core.somaxconn = 4096
- 指定系统全局级别上,每个监听socket的最大backlog值。如果你在listen()函数中指定的backlog值超过了somaxconn的设定,那么实际的backlog值将被限制为somaxconn的值。
- tcp_max_syn_backlog:
- 当系统正在处理大量的TCP连接时,这个参数控制着系统为半开(尚未完成三次握手)的TCP连接队列可以保持的最大数量。
sysctl net.ipv4.tcp_max_syn_backlog net.ipv4.tcp_max_syn_backlog = 512
- 当系统正在处理大量的TCP连接时,这个参数控制着系统为半开(尚未完成三次握手)的TCP连接队列可以保持的最大数量。
- tcp_synack_retries:
- 此参数定义了在放弃响应一个新的连接请求前,内核应该重新发送SYN+ACK包的次数。这个参数对于防止半开连接(SYN Flood 攻击)极为重要。
-
sysctl net.ipv4.tcp_synack_retries net.ipv4.tcp_synack_retries = 2
- tcp_abort_on_overflow
- 当服务器的监听队列溢出时,如果此参数设置为1,系统将向客户端发送RST(重置),而不是忽视接收到的SYN。这有助于客户端更快地了解到服务端的监听队列已溢出。
-
sysctl net.ipv4.tcp_abort_on_overflow net.ipv4.tcp_abort_on_overflow = 0
- tcp_rmem
- 接受缓冲区大小,它有三个值,分别表示最小值、默认值和最大值(单位:字节)。
-
sysctl net.ipv4.tcp_rmem net.ipv4.tcp_rmem = 4096 87380 6291456
- tcp_wmem
- 发送缓冲区的大小。它有三个值,对应最小值、默认值和最大值(单位:字节)
-
sysctl net.ipv4.tcp_wmem net.ipv4.tcp_wmem = 4096 16384 4194304
- tcp_sack
- 是否启用SACK,1 表示开启,0 表示关闭
-
sysctl net.ipv4.tcp_sack net.ipv4.tcp_sack = 1
- tcp_dsack
- 释放启用D-SACK,1 表示开启,0 表示关闭
-
sysctl net.ipv4.tcp_dsack net.ipv4.tcp_dsack = 1
- tcp_available_congestion_control
- 显示当前系统支持的拥塞控制算法
-
sysctl net.ipv4.tcp_available_congestion_control net.ipv4.tcp_available_congestion_control = cubic reno
- tcp_congestion_control
- 显示当前系统正在使用的拥塞控制算法
-
sysctl net.ipv4.tcp_congestion_control net.ipv4.tcp_congestion_control = cubic
- tcp_fastopen
- 0:禁用 TCP Fast Open。
- 1:允许作为客户端使用 TCP Fast Open。
- 2:允许作为服务器接受带有 TCP Fast Open 的 SYN 包。
- 3:允许作为客户端和服务器使用 TCP Fast Open。
-
sysctl net.ipv4.tcp_fastopen net.ipv4.tcp_fastopen = 0
- tcp_max_tw_buckets
- 指定系统中TIME_WAIT状态的TCP连接的最大数量。当系统中的TIME_WAIT连接数超过这个值时,新的TIME_WAIT连接将不会被创建,而是被立即关闭。
-
sysctl net.ipv4.tcp_max_tw_buckets net.ipv4.tcp_max_tw_buckets = 5000
- somaxconn :