网络运输层:TCP协议详解(一)

运输层基础内容:

计算机网络之运输层-CSDN博客

TCP参考:

(4 封私信) TCP协议详解 - 知乎

TCP报文段的首部格式

TCP 虽然是面向字节流的,但 TCP 传送的数据单元却是报文段。一个 TCP 报文段分为首部和数据两部分。TCP 报文段首部的前20个字节是固定的,后面有4n字节是根据需要而增加的可选项(n是整数)。因此 TCP 首部的最小长度是20字节。

逐字段详解

1. Source Port(源端口):16位

属性 说明
长度 2字节(16位)
作用 标识发送端应用程序
范围 0-65535
分类 0-1023(知名端口),1024-49151(注册端口),49152-65535(动态/私有端口)

注意:客户端通常使用临时端口(49152-65535),服务器使用知名端口(如80、443)。


2. Destination Port(目的端口):16位

属性 说明
长度 2字节(16位)
作用 标识接收端应用程序

端口与进程的关系

复制代码
TCP报文 → 目的端口=80 → 操作系统交给HTTP进程
TCP报文 → 目的端口=443 → 操作系统交给HTTPS进程

3. Sequence Number(序列号):32位

属性 说明
长度 4字节(32位)
作用 标识本数据段第一个字节的序号
范围 0 - 4,294,967,295(2^32-1)

核心作用

  • 按序交付:接收方能按序列号重组乱序到达的数据

  • 去重:重复的序列号表示重复数据,可丢弃

  • 标识丢失:缺失的序列号表示数据丢失

初始序列号(ISN,Initial Sequence Number)

  • 连接建立时,双方随机生成ISN

  • 第一个SYN包消耗一个序列号

  • 后续数据包的序列号 = ISN + 已发送字节数

示例

复制代码
ISN = 1000
发送100字节 → 序列号=1000(本段覆盖1000-1099)
下一个段 → 序列号=1100

回绕问题:32位序列号最大约42亿,高速网络可能在短时间内用完。TCP通过时间戳选项解决。


4. Acknowledgment Number(确认号):32位

属性 说明
长度 4字节(32位)
作用 期望下次收到的序列号
有效条件 ACK标志位=1

含义 :确认号=N,表示序号小于N的所有字节都已成功收到,下次期望收到序号N。

示例

复制代码
发送方收到 ACK=1100 → 表示对方已收到0-1099,下次期望1100

累积确认:一个ACK可以确认之前所有连续的数据,不需要为每个包单独确认。


5. Data Offset(数据偏移):4位

它指出 TCP 报文段的数据起始处距离 TCP 报文段的起始处有多远。这个字段实际上是指出 TCP 报文段的首部长度。

属性 说明
长度 4位
单位 32位字(4字节)
作用 指示TCP头部的总长度
最小值 5(5×4=20字节)
最大值 15(15×4=60字节)

计算方式

复制代码
TCP头部长度 = Data Offset × 4 字节

示例

  • Data Offset=5 → 头部20字节(无选项)

  • Data Offset=8 → 头部32字节(有12字节选项)


6. Reserved(保留位):3位

属性 说明
长度 3位
状态 必须为0
用途 保留给未来使用

7. Flags(标志位):9位

这是TCP最关键的字段之一,每个标志位都是一个布尔开关。

标志 全称 位位置 说明
NS Nonce Sum bit 8 ECN保护标志(RFC 3540)
CWR Congestion Window Reduced bit 7 拥塞窗口已减小
ECE ECN Echo bit 6 拥塞通知回显
URG Urgent bit 5 紧急指针有效
ACK Acknowledgment bit 4 确认号有效(除首个SYN外通常为1)
PSH Push bit 3 立即推送,不要缓存
RST Reset bit 2 强制重置连接
SYN Synchronize bit 1 同步序列号,用于建立连接
FIN Finish bit 0 发送方已无数据,请求关闭

下面6个主要控制位,用来说明本报文段的性质

  • 紧急 URG(URGent) 当 URG=1 时,表明紧急指针字段有效。它告诉系统此报文段中有紧急数据,应尽快传送(相当于高优先级的数据),而不是按原先的排队顺序来传送,现已很少使用。
  • 确认 ACK(ACKnowledgment) 仅当 ACK=1 时确认号字段才有效。当 ACK=0 时,确认号无效。TCP 规定,在连接建立后所有传送的报文段都必须把 ACK 置1。
  • 推送 PSH(Push) 提示接收方立即将数据交给应用层,不要等缓冲区满了再交。现代TCP栈通常忽略。
  • 复位 RST(ReSeT) 当 RST=1 时,表明 TCP 连接中出现严重差错(如由于主机崩溃或其他原因),必须释放连接,不经过正常挥手,然后再重新建立运输连接。
  • 同步 SYN(SYNnchronization) 在连接建立时用来同步序号。当 SYN=1 而 ACK=0 时,表明这是一个连接请求报文段。对方若同意建立连接,则应在响应的报文段中使 SYN=1 和 ACK=1。
  • 终止 FIN(FINis) 连接关闭时使用。FIN=1表示发送方已无数据发送。消耗一个序列号。

8. Window(窗口):16位

属性 说明
长度 2字节(16位)
作用 流量控制,告知对方自己的接收缓冲区剩余大小
单位 字节
范围 0 - 65535

核心作用

复制代码
发送方已发未确认的数据量 ≤ 对方的Window

零窗口:Window=0时,发送方停止发送,但会定期发送零窗口探测,防止死锁。

窗口缩放 :Window字段只有16位(最大65535),高速网络中不够用。通过Window Scale选项,可将窗口左移最多14位,理论最大窗口约1GB。


9. Checksum(校验和):16位

属性 说明
长度 2字节(16位)
覆盖范围 TCP伪头部 + TCP头部 + TCP数据
算法 16位反码求和
强制 TCP强制要求(UDP可选)

伪头部(Pseudo Header):只在计算校验和时构造,不是TCP报文的一部分。

复制代码
 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 Address                          |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|                    Destination Address                        |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|  Zero  |  Protocol (6)        |         TCP Length           |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
  • Source/Destination Address:从IP头部取来

  • Protocol:固定6(TCP)

  • TCP Length:TCP头部+数据的长度

为什么需要伪头部:防止数据被错误投递。校验和验证了"这个TCP包确实是发给我的"。


10. Urgent Pointer(紧急指针):16位

属性 说明
长度 2字节(16位)
作用 指向紧急数据的最后一个字节
有效条件 URG标志位=1
单位 字节

含义:Urgent Pointer = 紧急数据在TCP数据流中的偏移量(从序列号开始算)。

示例

复制代码
Sequence Number = 1000
Urgent Pointer = 50
→ 紧急数据覆盖 1000 到 1049 字节

现状:URG机制设计复杂,容易出错,实际使用很少。现代应用通常用带外数据或其他方式处理紧急需求。


11. Options(选项):可变长度(0-40字节)

属性 说明
长度 0-40字节
单位 字节,必须是8的倍数
作用 扩展TCP功能

常见选项

选项 类型值 长度 说明
MSS 2 4字节 告知对方自己能接收的最大段大小
Window Scale 3 3字节 窗口缩放因子,左移位数
Timestamp 8 10字节 时间戳,用于RTT计算和防回绕
SACK Permitted 4 2字节 告知对方支持SACK
SACK 5 变长 选择性确认,告知对方哪些数据已收到
NOP 1 1字节 空操作,用于对齐填充

MSS选项

复制代码
Kind=2, Len=4, Value=MSS
告知对方:我能接收的最大TCP段大小(不含TCP头)
以太网典型值:1460字节(1500 - 20IP头 - 20TCP头)

Window Scale选项

复制代码
Kind=3, Len=3, Shift=7
窗口实际大小 = Window字段 << Shift
Shift最大14,理论最大窗口约1GB
仅在SYN包中协商

Timestamp选项

复制代码
Kind=8, Len=10, TSval=发送方时间戳, TSecr=回显时间戳
作用:
1. RTTM:精确计算RTT
2. PAWS:防止序列号回绕

SACK选项

复制代码
选择性确认:告诉对方“我收到了1000-2000和3000-4000,但缺2000-3000”
避免重传整个窗口,只重传丢失的部分

12. Padding(填充):可变长度

属性 说明
长度 0-3字节
全0
作用 确保TCP头部是32位的整数倍

TCP头部字段速查表

字段 长度 位置(字节偏移) 作用
Source Port 16位 0-1 发送端应用程序标识
Destination Port 16位 2-3 接收端应用程序标识
Sequence Number 32位 4-7 本段第一个字节的序号
Acknowledgment Number 32位 8-11 期望下次收到的序号
Data Offset 4位 12高4位 头部长度(单位32位字)
Reserved 3位 12低3位 保留为0
Flags 9位 12-13 控制标志(SYN/ACK/FIN等)
Window 16位 14-15 接收窗口大小
Checksum 16位 16-17 校验和(含伪头部)
Urgent Pointer 16位 18-19 紧急数据偏移
Options 可变 20起 扩展功能(MSS/时间戳/SACK)
Padding 可变 选项后 对齐到32位边界

一句话总结:TCP报文头通过端口号区分应用,通过序列号和确认号实现可靠有序传输,通过窗口字段实现流量控制,通过标志位管理连接状态,通过校验和保证数据完整性,通过选项扩展功能------这20到60字节的头部,承载了TCP所有复杂能力的核心。

难道每个字节都有序列号吗?

你这个问题问得非常精准,抓住了TCP设计中的一个关键细节。

答案是:是的,逻辑上每个字节都有一个独立的序列号。

但千万别理解为"TCP会给每个字节添加一个8字节的序列号字段"那样冗余。它是通过起始序列号 + 长度的方式来标注一个段的。


1. 具体原理:不是标每个字节,而是标"段"的头和尾

假设初始序列号(ISN) = 1000,要发送5000字节的数据。TCP会把它切成多个段(比如每个段1460字节):

  • 段1 :序列号 = 1000,数据长度 = 1460 → 表示它携带了 字节 1000 ~ 2459

  • 段2 :序列号 = 2460,数据长度 = 1460 → 表示携带 字节 2460 ~ 3919

  • 段3 :序列号 = 3920,数据长度 = 1460 → 携带 字节 3920 ~ 5379

  • 段4 :序列号 = 5380,数据长度 = 620 → 携带 字节 5380 ~ 5999

你看,每个段的头部只存了一个起始序列号 (4字节),并没有为每个字节单独存序列号。但因为知道这个段的长度,就可以推导出它包含的所有字节的序列号

2. 那为什么说"每个字节有序列号"?

因为确认、重传、重排这些可靠机制,都需要精确到字节级。否则无法处理以下场景:

  • 部分ACK:对方收到了字节 1000-2459,但只收到了 2460-3000(后面的丢失了)。它回复 ACK=3001,意思是"我期待你的下一个字节是3001" ------ 这精确到某个字节。

  • 丢包重传:知道丢了具体从哪个字节开始的段。

  • 保序:接收方能根据每个字节的归属编号,把乱序到达的段正确拼接。

所以"每个字节有序列号"是一个逻辑抽象 ,便于理解机制。实现上是通过 起始序列号 + 段长度 来隐式完成的。


3. 一个形象的类比:电影院座位的排号

想象一排电影院座位,从第1排第1座开始连续编号:

  • 你买的票上只写着 "起始座位号:101""连续5个座位"

  • 你不会拿到一张写着 101,102,103,104,105 每个座位号的票。

  • 但检票员(TCP接收方)知道:101,102,103,104,105 这些座位都属于你,必须按顺序挨着坐(保序)。


4. 为什么要设计成这样,而不是只给每个段编号?

  • 节省开销:每个字节都附一个序列号,头部会膨胀几十倍。

  • 支持任意大小的段:TCP可以动态调整段大小(根据MSS、窗口等),无需改变序列号机制。

  • 处理部分ACK:如果只给段编号,当接收方收到半个段的内容(仍然可能,因为IP分片等原因),无法表达"我只缺这个段里的第50~100个字节"。

正是因为序列号的粒度是字节,TCP才能提供可靠的、面向字节流的服务。如果粒度是"段",那就变成数据报模式(类似UDP加个确认),无法实现真正的流式可靠性。
那tcp按序拼接时,不也只能按照段来,因为并不是每个字节真的都带一个序列号!

你的这个思考非常敏锐,而且完全正确。

在底层实现的"硬拼接"环节,TCP确实只能按照"段"来操作。 你看到的"按字节序号拼接"其实是操作系统和协议栈为了逻辑清晰而做的一个抽象

1. 为什么书上非要说"按字节"?

因为"段"是运输单位,而应用层读数据时,想要的是连续的字节流,不想关心段的分界。

当你调用 read() 时,操作系统要做一件事:把缓冲区里那些可能不连续的"段",拼接成一个连续的字节数组,然后交给应用程序。

  • 输入:缓冲区里有 段A [1000-1099)和 段C [1100-1199) 两个物理上分开的块。(中间缺了段B)

  • 动作 :TCP协议栈检查发现缺了1099到1100这个缝隙,于是不会 做拼接。read() 会阻塞或返回已收到的1000-1099。

  • 等段B到达后

    • 段B (SEQ=1099, Len=1) 到达。注意,段B可能只有1个字节。

    • 操作系统把段B填入那个缝隙。

    • 现在缓冲区里,1000-1099(段A)、1099(段B)、1100-1199(段C)在物理地址上可能依然是三个分开的内存块。

    • 但是,当应用层调用 read(1000, 200) 时,操作系统内核会把这些物理上分散的内存块 ,拷贝到应用程序的连续内存中。

系统关注的是离散的字节序号断点(1099) ,但在存储层面,它是以为单位存放这些数据的。

TCP有哪些状态

TCP是一个状态机 ,连接从建立到关闭的全过程,会在11种状态之间转换。理解这些状态,是排查网络问题(如连接超时、端口占用、CLOSE_WAIT堆积)的基础。


一、TCP状态总览(11种)

状态 中文名 角色 说明
CLOSED 关闭 初始 无连接状态(理论状态,实际看不到)
LISTEN 监听 服务器 等待客户端连接请求
SYN_SENT 同步已发送 客户端 已发SYN,等待服务器SYN+ACK
SYN_RCVD 同步已收到 服务器 已收到SYN,已发SYN+ACK,等待客户端ACK
ESTABLISHED 已建立 双方 连接建立成功,可以传输数据
FIN_WAIT_1 终止等待1 主动关闭方 已发FIN,等待ACK或对方的FIN
FIN_WAIT_2 终止等待2 主动关闭方 已收到ACK,等待对方FIN
CLOSE_WAIT 关闭等待 被动关闭方 收到FIN,已发ACK,等待应用层关闭
LAST_ACK 最后确认 被动关闭方 已发FIN,等待对方最后的ACK
TIME_WAIT 时间等待 主动关闭方 收到FIN并发了ACK,等待2MSL后关闭
CLOSING 关闭中 双方 同时关闭时,双方都发了FIN,等待ACK

二、TCP状态转换图


三、各状态详解

1. CLOSED(关闭)

属性 说明
含义 无连接状态,初始状态
特点 理论状态,实际在socket中看不到
转换 LISTEN(服务器),SYN_SENT(客户端)

2. LISTEN(监听)

属性 说明
含义 服务器等待客户端连接请求
特点 只出现在服务器端
转换 收到SYN → SYN_RCVD

查看命令

复制代码
netstat -an | grep LISTEN
# 或
ss -lnt

3. SYN_SENT(同步已发送)

属性 说明
含义 客户端已发送SYN,等待服务器SYN+ACK
特点 只出现在客户端
转换 收到SYN+ACK → ESTABLISHED;收到RST → CLOSED

4. SYN_RCVD(同步已收到)

属性 说明
含义 服务器已收到SYN,已发SYN+ACK,等待客户端ACK
特点 出现在服务器端
转换 收到ACK → ESTABLISHED;收到RST → CLOSED

半连接队列:SYN_RCVD状态的连接存放在半连接队列(SYN Queue)中,收到ACK后移入全连接队列(Accept Queue)。


5. ESTABLISHED(已建立)

属性 说明
含义 连接建立成功,可以传输数据
特点 双方正常的通信状态
转换 主动关闭 → FIN_WAIT_1;被动关闭 → CLOSE_WAIT

查看命令

复制代码
netstat -an | grep ESTABLISHED
ss -tn state established

6. FIN_WAIT_1(终止等待1)

属性 说明
含义 主动关闭方已发送FIN,等待ACK或对方的FIN
特点 出现在主动关闭方
转换 收到ACK → FIN_WAIT_2;收到FIN → CLOSING;收到FIN+ACK → TIME_WAIT

7. FIN_WAIT_2(终止等待2)

属性 说明
含义 主动关闭方已收到ACK,等待对方FIN
特点 出现在主动关闭方
转换 收到FIN → TIME_WAIT

注意:如果对方一直不发送FIN(比如程序忘记关闭socket),FIN_WAIT_2状态会一直存在。


8. CLOSE_WAIT(关闭等待)⚠️ 常见问题

属性 说明
含义 被动关闭方收到FIN,已发ACK,等待应用层调用close()
特点 出现在被动关闭方
转换 应用层调用close() → LAST_ACK

⚠️ CLOSE_WAIT堆积是常见问题

  • 原因:服务器代码没有正确调用close(),或者进程卡住了

  • 后果:大量CLOSE_WAIT连接耗尽文件描述符,无法建立新连接

  • 解决:检查代码,确保所有socket都正确关闭

查看命令

复制代码
netstat -an | grep CLOSE_WAIT | wc -l   # 统计数量

9. LAST_ACK(最后确认)

属性 说明
含义 被动关闭方已发送FIN,等待对方最后的ACK
特点 出现在被动关闭方
转换 收到ACK → CLOSED;超时 → CLOSED

10. TIME_WAIT(时间等待)⚠️ 常见问题

属性 说明
含义 主动关闭方收到FIN并发了ACK,等待2MSL后关闭
特点 出现在主动关闭方
持续时间 2MSL(Maximum Segment Lifetime),通常2分钟
转换 2MSL超时 → CLOSED

TIME_WAIT的作用

  1. 确保最后的ACK能被对方收到:如果ACK丢失,对方会重发FIN,TIME_WAIT状态可以响应这个重发的FIN

  2. 让旧连接的数据包在网络中消失:防止旧连接的数据干扰新连接

⚠️ TIME_WAIT堆积

  • 原因:高并发短连接场景(如Web服务器频繁关闭连接)

  • 影响:占用端口资源,可能导致无可用端口

  • 解决

    • 调整内核参数net.ipv4.tcp_tw_reuse(允许复用TIME_WAIT连接)

    • 调整net.ipv4.tcp_tw_recycle(快速回收,已废弃,NAT环境会有问题)

    • 使用长连接,减少连接建立/关闭次数

查看命令

复制代码
netstat -an | grep TIME_WAIT | wc -l
ss -tan state time-wait

11. CLOSING(关闭中)

属性 说明
含义 双方同时发送FIN,同时关闭
特点 罕见,出现在双方几乎同时调用close()
转换 收到ACK → TIME_WAIT

同时关闭场景

复制代码
客户端                             服务器
   |                                  |
   |--------  FIN ------------------->|
   |                                  |
   |<-------  FIN --------------------|
   |                                  |
   |--------  ACK ------------------->|
   |                                  |
   |<-------  ACK --------------------|

四、状态转换速查表

当前状态 收到包 下一状态
LISTEN SYN SYN_RCVD
SYN_SENT SYN+ACK ESTABLISHED
SYN_RCVD ACK ESTABLISHED
ESTABLISHED FIN(主动) FIN_WAIT_1
ESTABLISHED FIN(被动) CLOSE_WAIT
FIN_WAIT_1 ACK FIN_WAIT_2
FIN_WAIT_1 FIN CLOSING
FIN_WAIT_2 FIN TIME_WAIT
CLOSE_WAIT close() LAST_ACK
LAST_ACK ACK CLOSED
TIME_WAIT 2MSL超时 CLOSED

五、排查命令汇总

命令 作用
netstat -an 查看所有连接状态
`netstat -an grep LISTEN`
`netstat -an grep ESTABLISHED`
`netstat -an grep CLOSE_WAIT`
`netstat -an grep TIME_WAIT`
ss -tln 查看TCP监听端口
ss -tan state established 查看ESTABLISHED连接
ss -tan state time-wait 查看TIME_WAIT连接
lsof -i :端口号 查看端口被哪个进程占用

六、常见问题与状态对应

问题现象 相关状态 原因
服务连不上 LISTEN 服务没启动或端口没监听
连接超时 SYN_SENT 网络不通或防火墙丢SYN
半连接攻击 SYN_RCVD SYN Flood,半连接队列满
服务正常但连接被拒 ESTABLISHED 全连接队列满,新连接被丢
大量CLOSE_WAIT CLOSE_WAIT 代码没正确关闭socket
端口被占用 TIME_WAIT 高并发短连接,端口耗尽

三次握手

TCP建立连接的过程被称为三次握手(Three-way Handshake),这是TCP协议中最基础、最重要的机制之一。


为什么需要三次握手?

在理解过程之前,先明确目的:三次握手是为了在不可靠的网络基础上,可靠地建立一条连接,并同步双方的初始序列号。

具体要解决三个问题:

  1. 确认双方的收发能力都正常

  2. 同步双方的初始序列号(ISN,Initial Sequence Number)

  3. 交换TCP选项(如MSS、窗口缩放、SACK等)

流程图如下所示:

TCP 是面向连接、可靠传输 的协议,传输数据前必须先建立连接 ,保证双方发送能力、接收能力都正常。涉及三个核心标志位:

  • SYN:同步序列编号(请求连接)
  • ACK:确认应答(收到对方报文了)

还有序列号 seq确认号 ack

  • seq=x:我方本次发送数据包的序号
  • ack=y我已经收到了你序号为 y-1 的数据,下一次请你发 y

完整三次握手全过程(逐轮拆解)

默认:客户端主动发起连接,服务器被动监听

第一次握手:客户端 → 服务器

报文SYN=1,seq=x

  1. 客户端状态:CLOSEDSYN_SENT
  2. 动作:客户端向服务器发送连接请求报文 ,只有 SYN 标志,没有数据
  3. 含义: 你好,我想和你建立连接,我的初始序列号是 x。
  4. 服务器收到后验证:确认服务器的接收能力正常、客户端的发送能力正常

第二次握手:服务器 → 客户端

报文SYN=1,ACK=1,ack=x+1,seq=y

  1. 服务器状态:LISTENSYN_RCVD
  2. 动作:服务器回复双重报文
    • ACK=1,ack=x+1:确认收到客户端的 SYN,下一次客户端从 x+1
    • SYN=1,seq=y:服务器自己的连接请求,服务器也要同步序列号
  3. 含义: 收到你的连接请求了,同意连接。我的初始序列号是 y。
  4. 客户端收到后验证:确认客户端发送、接收正常;服务器发送、接收都正常

第三次握手:客户端 → 服务器

报文ACK=1,ack=y+1,seq=x+1

  1. 客户端状态:SYN_SENTESTABLISHED
  2. 服务器收到后:SYN_RCVDESTABLISHED
  3. 动作:客户端发送确认报文,依然不带数据
    • ack=y+1:确认收到服务器的 SYN,下次服务器从 y+1
    • seq=x+1:客户端下一轮数据起始序号
  4. 含义: 我也收到你的回复了,连接正式建立完毕。
  5. 自此双方双向通道打通,开始传输业务数据。

三、极简流程图(死背版)

  1. 客户端SYN,seq=x
  2. 服务器SYN+ACK,ack=x+1,seq=y
  3. 客户端ACK,ack=y+1,seq=x+1

四、面试高频灵魂问题:为什么是三次,不能两次?

1. 防止历史过期连接请求阻塞服务器

最经典标准答案:网络会延迟丢包、旧报文滞留

  • 假设只有两次握手 :客户端很久以前发的过期 SYN 请求 ,慢悠悠传到服务器。服务器收到 SYN,直接第二次握手 ACK 同意连接 ,进入建立连接状态。但客户端早就不要这个连接了,不会理服务器。服务器就会一直空占连接、消耗内存资源,大量旧包堆积会直接打垮服务器。

  • 三次握手多了客户端最后一次 ACK :过期的旧 SYN,服务器回复 SYN+ACK 后,客户端不会回第三次 ACK 。服务器收不到第三次 ACK,超时就自动释放连接,不会被无效连接占用

2. 双向序列号同步

TCP 是全双工通信:双方都要发、都要收。

  • 客户端需要自己的 seq、确认服务器的 seq
  • 服务器需要自己的 seq、确认客户端的 seq双方发送序列号、接收序列号都必须同步,最少需要 3 轮交互才能闭环。

3. 一句话总结

两次握手:服务器无法判断客户端是否收到自己的报文 ,且防不住脏旧连接。三次握手:双方收发能力全部验证完毕,序列号完全同步


五、状态机全程汇总

  1. 客户端:CLOSEDSYN_SENTESTABLISHED
  2. 服务器:LISTENSYN_RCVDESTABLISHED

六、易混点区分

  1. 三次握手全程都不传输业务数据,只建通道;数据在三次握手完成后才开始发。
  2. SYN 报文不携带数据 ,但消耗一个序列号;ACK 纯确认报文不携带数据,不消耗序列号
    第一次握手的seq=x,为啥第二次握手回复的ack=x+1?

直接回答:因为第一次握手发送的SYN包本身,也要被算进序列号里。它虽然不携带用户数据,但作为一个"控制命令",消耗了一个序列号。所以接收方必须回复x+1,来表示"我已收到了你的SYN命令,下一个希望收到序列号为x+1的数据"。

下面从三个层面来详细解释:

核心规则:SYN和FIN要占一个序列号

这是理解这个问题的根本。TCP协议规定,SYN和FIN标志为1的包,虽然不携带应用层数据,但必须消耗一个序列号

  • 普通数据包:发送了100字节,序列号就增加100。

  • SYN包(不携带数据) :序列号只增加1 。这1个序列号,代表的就是"发起连接"这个行为本身。

为什么?

为了让确认机制能覆盖整个数据流,包括连接和关闭事件。接收方回复Ack=x+1,不仅仅是在说"收到你的序列号x",更是在说:"我确认你发起的**建立连接请求(SYN)**已经成功被我收到了。"

MSS

MSSMaximum Segment Size(最大段大小)的缩写。

它是 TCP 协议中一个非常重要的参数,决定了在不发生 IP 分片的情况下,单个 TCP 段(Segment)里能携带的最大有效载荷(Payload,即实际数据)的字节数

注意:MSS 只计算 TCP 头部后面的数据部分,不包括 TCP 头部和 IP 头部。


一、为什么需要 MSS?

这要从网络分层和 MTU 说起。

1. 链路层的限制:MTU

以太网中,一个数据帧的最大大小通常是 1500 字节(MTU,Maximum Transmission Unit)。

2. IP 层不分片的原则

一个 IP 包(包括 IP 头和 TCP 段)如果超过 1500 字节,IP 层可以把它拆成多个分片(Fragment),但这会带来问题:

  • 如果任何一个分片丢失,整个 IP 包都要重传

  • 分片会增加路由器的负担,降低效率

更好的做法 :在 TCP 层就控制好每个段的大小,让它装进 IP 包后不超过 MTU,从而避免 IP 分片。


二、MSS 的计算公式

为了让 IP 包不超过 MTU:

复制代码
IP包总大小 = IP头部 + TCP头部 + TCP数据 ≤ MTU

所以:TCP数据 ≤ MTU - IP头部 - TCP头部

因此:

复制代码
MSS = MTU - IP头部(通常20字节) - TCP头部(通常20字节)

默认情况下(以太网 MTU=1500):

复制代码
MSS = 1500 - 20 - 20 = 1460 字节

这是最常见的 MSS 值。


三、MSS 的协商过程

MSS 不是任意指定的,而是在 TCP 三次握手期间 由双方协商决定的。

复制代码
客户端 → 服务器: SYN, MSS=1460 (我的最大能收1460)
服务器 → 客户端: SYN+ACK, MSS=1400 (抱歉,我只能收1400)
客户端 → 服务器: ACK

最终双方使用 min(1460, 1400) = 1400 作为连接的 MSS

关键点:

  • MSS 选项只在 SYN 包中出现

  • 每一方通告的是自己接收能力(我能收多大的段)

  • 实际发送时,发送方使用对端通告的 MSS 作为自己的发送上限


四、一个常见的困惑:MSS vs MTU

概念 定义 所在层 典型值(以太网)
MTU 链路层能传输的最大IP包大小(包含IP头) 链路层/IP层 1500字节
MSS TCP段中数据的最大字节数(不含IP头、TCP头) TCP层 1460字节

关系图

复制代码
|<- 链路层帧(最多1518)->|
  |<-       MTU=1500     ->|
    |<- IP头20 ->|<- TCP头20 ->|<- MSS=1460 ->|
                  |<------ TCP段 ------>|

五、MSS 过小或过大的影响

1. MSS 过大

  • 超过路径 MTU(PMTU,Path MTU),会导致 IP 分片

  • 如果 IP 分片被防火墙丢弃或丢失率高,连接会卡死

2. MSS 过小

  • 传输效率低:同样的数据量要分成更多段

  • 头部开销占比大:每个段都有 TCP + IP 头部(至少 40 字节)

3. 最佳实践

  • 使用 PMTU 发现:TCP 会探测路径上最小的 MTU,动态调整 MSS

  • 开启 DF 标志(Don't Fragment),当包太大时路由器返回 ICMP 错误,TCP 会缩小 MSS


六、实际抓包示例

复制代码
// 三次握手协商 MSS
14:32:01.123 IP 192.168.1.100.54321 > 93.184.216.34.80: Flags [S], 
     seq 1000000, win 65535, options [mss 1460,nop,wscale 7], length 0

14:32:01.234 IP 93.184.216.34.80 > 192.168.1.100.54321: Flags [S.], 
     seq 2000000, ack 1000001, win 8192, options [mss 1400], length 0

// 后续数据传输的最大段 = min(1460, 1400) = 1400 字节

七、与 MSS 相关的常见问题

Q:为什么有时候抓包看到大于 MSS 的段?

A:可能原因:

  • 使用了 TCP Segmentation Offload (TSO),网卡硬件分包

  • IP 层分片(但 DF 标志通常禁止)

  • 抓包位置在分片之后

Q:MSS 能超过 1460 吗?

A:可以,如果 MTU 更大(如 jumbo frame,MTU=9000,则 MSS=8960)。但需要整个路径都支持。

Q:MSS 为 0 是什么意思?

A:SYN 包中 MSS=0 告诉对方"不要发任何数据",用于某些特殊场景(如端口扫描探测)。

Q:应用层一次 send 1MB 数据,TCP 会发一个 1MB 的段吗?

A:不会。TCP 会按 MSS 把数据拆成多个段,每个段最大为 MSS。


总结一句话

MSS 是 TCP 协商出的"单个段能装的最大货物量",默认 1460 字节,目的是让 IP 包不超过链路层的 MTU,避免分片,提高效率。

四次挥手

如下:

TCP 是全双工 :连接建立后,客户端↔服务器两条独立通道

  • A 给 B 发数据
  • B 给 A 发数据两条流互不干扰 ,关闭的时候两边都要单独关闭 ,所以才需要四次挥手

用到标志位:

  • FIN :结束报文,我没有数据要发了,关闭写通道
  • ACK:确认应答

序列号依旧:

  • seq=u 发送方序列号
  • ack=v 确认号:我收到你v-1的数据了

四次挥手完整全过程(逐轮拆解)

默认:客户端主动断开连接,服务器被动关闭

第一次挥手:客户端 → 服务器

FIN=1,seq=u

  1. 客户端状态:ESTABLISHEDFIN_WAIT_1
  2. 含义:

我数据发完了,我这边不再给你发数据了,关闭客户端→服务器的写通道。

  1. 服务器收到后:知道客户端要断开了,但服务器还有可能要给客户端发剩余数据,不能立刻关。

第二次挥手:服务器 → 客户端

ACK=1,ack=u+1,seq=v

  1. 服务器状态:ESTABLISHEDCLOSE_WAIT
  2. 含义:

收到你的断开请求了,我确认了。你那边可以先关掉了。

  1. 关键点:此时单向关闭完成客户端 → 服务器通道关闭 服务器 → 客户端通道还开着,服务器依旧可以给客户端发数据。

第三次挥手:服务器 → 客户端

(服务器数据全部发送完毕后)FIN=1,seq=v

  1. 服务器状态:CLOSE_WAITLAST_ACK
  2. 含义:

我数据也全部发完了,我这边也不给你发数据了,服务器→客户端通道关闭。

第四次挥手:客户端 → 服务器

ACK=1,ack=v+1,seq=u+1

  1. 客户端状态:FIN_WAIT_2TIME_WAIT
  2. 服务器收到后:LAST_ACKCLOSED
  3. 含义:

收到你的断开请求,全部确认,双向通道彻底关闭。

  1. 客户端等待2MSL 时间后,也进入 CLOSED

三、极简流程图(面试直接背)

  1. 客户端FIN,seq=u
  2. 服务器ACK,ack=u+1,seq=v
  3. 服务器FIN,seq=v
  4. 客户端ACK,ack=v+1,seq=u+1

四、全套状态机完整梳理

客户端状态流转

ESTABLISHEDFIN_WAIT_1(发第一次 FIN)→ FIN_WAIT_2(收到第二次 ACK)→ TIME_WAIT(收到第三次 FIN,发第四次 ACK)→ 等待 2MSL → CLOSED

服务器状态流转

ESTABLISHEDCLOSE_WAIT(收到第一次 FIN,发 ACK)→ LAST_ACK(数据发完,发 FIN)→ CLOSED(收到第四次 ACK)


五、灵魂高频问题

1. 为什么建立连接 3 次握手,断开要 4 次挥手?

最标准答案:

  1. 建立连接时 :服务器的SYNACK可以合并在一个报文里一起发给客户端,所以省一步,只要 3 次。
  2. 断开连接时 :服务器收到 FIN 后,可能还有剩余数据要发送 ,不能立刻发 FIN 关闭自己。所以ACK 确认自身 FIN 关闭 必须分开发,不能合并,因此多了一轮,变成 4 次。

一句话总结:

建连:服务器 SYN+ACK 合并 → 3 次断连:服务器 ACK、FIN 不能合并 → 4 次

2. 什么是 TIME_WAIT?为什么要等 2MSL?

客户端发完最后一次 ACK 后,不直接关闭,停留 2MSL

  1. 保证服务器一定收到了最后这个 ACK
  2. 防止网络滞留的旧报文流入下一次新连接,造成干扰

3. 什么是 2MSL?

MSL:报文最大生存时间2MSL = 报文去程最大时间 + 返程最大时间足够让网络里所有旧报文过期消失。

可靠传输

TCP 的可靠传输是通过多种机制的协同工作来保证数据能够准确、有序、无重复地送达对端。下面为你系统地讲解这些核心机制。

核心思路 :TCP 将应用层数据分割成多个 TCP 段 (Segment),并为每个段编号。接收方收到后要确认(ACK),发送方若未收到确认会重传。


1. 校验和 (Checksum) -- 保证数据完整

每个 TCP 段的头部和数据的错误检测。发送方计算出一个校验和并填入头部,接收方重新计算,如果一致则说明数据在传输中没有损坏,否则直接丢弃该段(发送方会因未收到 ACK 而重传)。

2. 序列号 (Sequence Number) -- 保证数据有序且不重复

  • 每个字节都有一个独立的序列号。

  • 初始序列号 (ISN) 在建立连接时随机生成。

  • 接收方根据序列号对段进行重排,确保交给应用层的数据顺序正确;同时也用于去重,丢弃已经接收过的段。

3. 确认应答 (ACK) -- 确认已收到

  • 接收方收到数据后,会回复一个 ACK 段 ,其中的 确认号 (Acknowledgment Number) 等于 期望收到的下一个字节的序列号

  • 例如:收到序列号 0-999 的数据,则回复确认号 1000,表示 "0-999 的字节已收到,下一个请从 1000 开始发送"。

4. 超时重传 (Retransmission Timeout, RTO)

发送方每发一个段,会启动一个定时器。如果在 RTO 时间内没有收到对应的 ACK,就认为该段丢失,并重新发送。

  • 关键:RTO 不能是固定值。TCP 使用 加权滑动平均 算法动态估算网络往返时间 (RTT),并据此动态调整 RTO,以适应网络变化。

5. 快速重传 (Fast Retransmit) -- 避免超时等待

  • 触发条件 :发送方收到 三个重复的 ACK(即连续三次确认同一个序列号)。

  • 含义:接收方在告诉发送方 "我缺了从某个序列号开始的数据,请立刻重传"。

  • 优点:不必等到超时,能更快修复丢包。

6. 流量控制 (Flow Control) -- 防止接收方被淹没

  • 通过 滑动窗口 实现。

  • 接收方在 ACK 中会携带 接收窗口 (rwnd),告诉发送方 "我的缓冲区还能接收多少字节"。

  • 发送方发送的总字节数(已发未确认部分)不能超过对方的接收窗口。

注意:rwnd 是动态变化的,当接收方应用层读取数据后,窗口会增大。

7. 拥塞控制 (Congestion Control) -- 防止网络过载

这是 TCP 保证网络不崩溃的重要机制,与流量控制不同(后者针对接收方能力)。主要包含四个阶段:

阶段 算法 说明
慢启动 指数增长拥塞窗口 (cwnd) 初始 cwnd 很小,每收到一个 ACK,cwnd 翻倍(实际是每个 RTT 翻倍),直到达到慢启动阈值 (ssthresh)
拥塞避免 线性增长 cwnd 到达 ssthresh 后,每收到一个 ACK 增加一个 MSS 的 1/cwnd(即每个 RTT 约增加 1 MSS),避免过快增长引发拥塞
快速重传 + 快速恢复 不回到慢启动 检测到丢包(例如 3 个重复 ACK)时:设置 ssthresh = cwnd/2,然后 cwnd = ssthresh + 3(快速恢复),线性增长,避免剧烈波动
超时丢包 重置到慢启动 若发生超时,则 ssthresh = cwnd/2,cwnd = 1 MSS,重新慢启动(较激进,说明网络拥塞严重)

整体交互示例

假设发送方从序列号 100 开始发送:

  1. 发送:数据 100-199 (seq=100), 200-299 (seq=200), 300-399 (seq=300)

  2. 接收方收到 200-299 和 300-399,但没收到 100-199

  3. 接收方回复 ACK=100(重复三次)

  4. 发送方触发快速重传,立即重传 100-199

  5. 接收方收齐后回复 ACK=400

  6. 窗口滑动,继续发送


总结

TCP 的可靠传输是 "校验 + 序列化 + 确认 + 重传 + 窗口控制" 的组合:

  • 校验和 → 防数据损坏

  • 序列号/ACK → 保序、去重、确认

  • 超时/快速重传 → 丢包恢复

  • 流量控制 (rwnd) → 不同接收方能力匹配

  • 拥塞控制 (cwnd) → 适应网络负载

这些机制一起让 TCP 在上层应用看来,像是一条可靠的、顺序无误的字节流通道。

滑动窗口和流量控制

参考:

tcpip协议第15讲:tcp滑动窗口、拥塞窗口以及拥塞控制原理介绍_哔哩哔哩_bilibili

神奇的滑动窗口 | TCP流量控制_哔哩哔哩_bilibili

所谓的"滑动",就是接收缓存的大小不断变化;"窗口"就是还能继续接收数据的缓存大小。

流量控制是接收方用来限制发送方发送速度的机制,目的是防止接收方的缓冲区被填满,导致数据被丢弃。


1. 为什么需要流量控制?

因为TCP是全双工 通信,双方都有发送和接收的能力。但接收方的应用程序可能处理数据比较慢(比如还在处理上一批数据,还没来得及调用read)。

如果不管不顾地狂发数据,接收方的内核接收缓冲区满了之后,后续到达的数据包就只能被丢弃。

流量控制要解决的就是这个问题:发送方发送数据的速度,必须匹配接收方应用程序读取数据的速度。


2. 核心机制:滑动窗口协议

TCP使用滑动窗口 来实现流量控制。每个TCP报文段头部都有一个 Window 字段,用来告知对端:"我现在还能接收多少字节的数据"。

接收窗口 (rwnd)

  • 定义:接收方当前可用的缓冲区大小。

  • 作用 :发送方不能发送超过rwnd字节的未确认数据。

发送方的发送窗口

发送方实际能发送的数据量由两个窗口共同决定:

复制代码
有效发送窗口 = min(rwnd, cwnd)
  • rwnd:接收窗口(流量控制)

  • cwnd:拥塞窗口(拥塞控制)


3. 详细工作过程

3.1 正常情况

  1. 接收方通告窗口大小 = 4000字节

  2. 发送方发送2000字节(未确认)

  3. 接收方收到数据,但应用程序只读了1000字节,缓冲区还有1000字节空闲

  4. 接收方回复ACK,同时通告窗口 = 2000字节(4000 - 2000 + 1000)

  5. 发送方根据新的窗口大小继续发送

3.2 零窗口 (Zero Window)

当接收方缓冲区满了,就会通告窗口 = 0。

此时发送方会停止发送数据 ,并启动一个持续计时器 。计时器超时后,发送方会发送一个零窗口探测报文,试探接收方的窗口是否已经打开。

3.3 糊涂窗口综合征 (Silly Window Syndrome)

这是一个需要避免的问题。当接收方每次只读入少量数据(比如1字节),然后通告一个很小的窗口(比如1字节),发送方也真的发送1字节数据,这样会导致网络效率极低。

解决方案

  • 接收方:不通告小窗口。只有当缓冲区可用空间达到一定阈值(如MSS或缓冲区的1/2)时,才通告非零窗口。

  • 发送方:不发送小数据段。可以累积数据直到满足一定条件(如达到MSS)再发送。Nagle算法就是做这个的。


4. 与拥塞控制的对比

对比项 流量控制 拥塞控制
目的 解决接收方处理不过来的问题 解决网络(中间路由器)处理不过来的问题
控制变量 接收窗口 (rwnd) 拥塞窗口 (cwnd)
作用于 单个TCP连接 所有TCP流(全局视角)
反馈来源 接收方的ACK包中的Window字段 网络中的丢包、延迟增加、ECN标记

最终发送窗口 = min(rwnd, cwnd)


5. 实际抓包观察

你可以用Wireshark抓包来观察流量控制过程:

  1. 查看tcp.window_size字段,看窗口如何动态变化

  2. 关注 Win=0 的包(零窗口)

  3. 关注 TCP ZeroWindowTCP ZeroWindowProbe 等提示

这些都能直观地看到流量控制的运作。


总的来说,流量控制是TCP可靠性的重要一环------它通过接收方控制窗口大小,避免了因接收方处理能力不足导致的数据丢失。

拥塞窗口和拥塞控制

参考:

tcpip协议第15讲:tcp滑动窗口、拥塞窗口以及拥塞控制原理介绍_哔哩哔哩_bilibili
"我为人人,人人为我"的TCP拥塞控制_哔哩哔哩_bilibili

拥塞控制如下所示:

你之前已经了解了流量控制 (解决接收方处理不过来的问题),而拥塞控制 解决的是另一个完全不同的问题:网络本身(中间路由器)处理不过来了

这两者经常被混淆,我把它们放在一起对比,你就能看得很清楚了。


一、核心思想

拥塞控制的目标:避免发送方发送数据过快,导致网络中的路由器缓存溢出,从而引发丢包。

关键变量拥塞窗口 (cwnd, Congestion Window)

  • 发送方能发送的数据量 = min(接收窗口rwnd, 拥塞窗口cwnd)

  • rwnd 是接收方的限制(流量控制)

  • cwnd 是网络本身的限制(拥塞控制)


二、拥塞窗口(cwnd)的直观理解

想象一条高速公路(网络):

  • 流量控制:收费站(接收方)说"我一次只能处理10辆车",这是接收方的能力。

  • 拥塞控制:道路本身(网络)说"这条路上同时跑100辆车会堵死",这是网络的容量。

**拥塞窗口(cwnd)**就是发送方猜测的"目前网络能容纳的、尚未被确认的数据量"。

  • cwnd 小 → 发送方认为网络比较空(或者正处在拥塞状态)

  • cwnd 大 → 发送方认为网络比较通畅

重点 :cwnd 是发送方自己维护的变量,不会在TCP包里传输。抓包看不到cwnd的值,只能通过发送模式推断。


三、拥塞控制的四个核心算法

1. 慢启动 (Slow Start)

目标:快速探测网络的可用带宽。

机制 :初始时 cwnd = 1 MSS10 MSS(现代TCP),每收到一个ACK,cwnd翻倍。

复制代码
发送过程:
第1轮:发1个包 → 收1个ACK → cwnd = 2
第2轮:发2个包 → 收2个ACK → cwnd = 4
第3轮:发4个包 → 收4个ACK → cwnd = 8
第4轮:发8个包 → cwnd = 16
...

停止条件 :当 cwnd >= ssthresh(慢启动阈值)时,进入拥塞避免。


2. 拥塞避免 (Congestion Avoidance)

目标:缓慢增长,避免突然拥塞。

机制 :每收到一个ACK,cwnd增加 1/cwnd 个MSS。实际上,每个RTT(一轮)cwnd增加1个MSS。

复制代码
cwnd = 8
发8个包 → 收8个ACK → 每个ACK增加1/8 → 总共增加1 → cwnd = 9
发9个包 → 收9个ACK → cwnd = 10
...

特点:线性增长,温和地试探网络的极限。


3. 快速重传 (Fast Retransmit)

触发条件 :发送方收到 3个重复的ACK

含义:接收方明确表示"我缺了某个包,请立刻重传"。

动作 :不等超时,立即重传丢失的包。这是对丢包的快速响应


4. 快速恢复 (Fast Recovery)

这是快速重传之后的状态,不同的TCP实现略有差异,但核心思想是:

  • 收到3个重复ACK时,网络还没有完全瘫痪(因为还能收到ACK)。

  • 动作

    1. ssthresh = cwnd / 2

    2. cwnd = ssthresh + 3(+3是因为收到了3个重复ACK,表示有3个包已离开网络)

    3. 进入拥塞避免(不是慢启动)

结果:丢包后,发送速率减半,但不会降到1,避免过度反应。


四、丢包后的两种情况对比

事件 发送方动作 cwnd变化 ssthresh变化
收到3个重复ACK 快速重传 + 快速恢复 cwnd = cwnd/2 + 3 ssthresh = cwnd/2
超时(RTO到期) 超时重传 cwnd = 1 MSS ssthresh = cwnd/2(丢包前的一半)

区别

  • 重复ACK → 丢包可能是个别现象,网络还能工作 → 降半速

  • 超时 → 网络可能严重拥塞 → 回到慢启动

六、流量控制 vs 拥塞控制(重要区分)

维度 流量控制 (Flow Control) 拥塞控制 (Congestion Control)
解决的问题 接收方处理不过来(缓冲区满) 网络处理不过来(路由器队列满)
控制变量 接收窗口 (rwnd) 拥塞窗口 (cwnd)
谁决定的 接收方(在ACK中通告) 发送方(自己推算)
抓包能看到吗 ✅ 能(TCP头部Window字段) ❌ 不能(只能推断)
代表机制 滑动窗口、零窗口探测 慢启动、拥塞避免、快速恢复
最终发送窗口 min(rwnd, cwnd) min(rwnd, cwnd)

一句话总结区别

流量控制是接收方说"你慢点,我快吃不消了";拥塞控制是网络说"你慢点,路上已经堵死了"。发送方两者都要听,取最小值。


七、常见的拥塞控制算法演进

算法 特点 适用场景
Tahoe 早期版本:慢启动+拥塞避免,超时或重复ACK都回到慢启动 历史版本
Reno 增加快速恢复(3个重复ACK只降半速) 有线网络
NewReno 改进快速恢复,能处理多个丢包 有线网络
CUBIC 使用三次函数增长,更激进 Linux默认,高速网络
BBR 基于带宽和延迟模型,而非丢包 高延迟、高丢包网络(如跨国、卫星)

目前Linux默认通常是 CUBIC ,Google大力推广的 BBR 在很多场景表现更好。


八、实际推断cwnd的方法(抓包)

虽然不能直接看到cwnd,但可以通过以下方式推断:

  • Time-Sequence图

    • 横轴:时间

    • 纵轴:序列号

    • 斜率 = 发送速率

    • 斜率变化点 = cwnd变化点

  • 计算发送速率

    复制代码
    cwnd ≈ 发送速率 × RTT

    在Wireshark中:Statistics → TCP Stream Graph → Throughput

  • 观察慢启动

    • 每个RTT内发出的包数量翻倍
  • 观察拥塞避免

    • 每个RTT内发出的包数量增加1
  • 观察丢包响应

    • 收到3个重复ACK后,发送速率立即减半

总结一句话

拥塞控制是TCP的"交通警察"------发送方通过维护拥塞窗口cwnd,使用慢启动、拥塞避免、快速重传、快速恢复等算法,动态感知网络负载,在保证不造成网络瘫痪的前提下,尽可能高地利用带宽。它与流量控制(接收方的能力限制)共同决定了TCP的实际发送速度。

相关推荐
汽车仪器仪表相关领域2 小时前
Kvaser Leaf Light HS v2 M12:5 针 M12 NMEA 2000 接口,海事与工业 CAN 总线测试的防水耐用之选
大数据·网络·人工智能·功能测试·安全性测试
爱吃芹菜炒肉2 小时前
Chapter 16: Power Management
服务器·c语言·网络·tcp/ip·pcie
运维行者_2 小时前
通过OpManager的Windows服务监控能力释放最佳IT网络性能
服务器·开发语言·网络·windows·web安全·php
爱学习的小囧2 小时前
ESXi性能历史怎么监控?2种方法,图形化+命令行全覆盖
java·linux·运维·服务器·网络·esxi·esxi8.0
g3voip3 小时前
SIP 对讲广播系统优质厂家与品牌推荐
tcp/ip·安全·信息与通信·调度
小草儿7993 小时前
gbase8s之onatpe备份与恢复性能测试
linux·服务器·网络
凯勒姆3 小时前
主流网络协议
网络·网络协议
淼淼爱喝水3 小时前
ensp- ACL 综合配置实验(附拓扑与完整步骤)
网络·智能路由器·ensp·acl
郝学胜-神的一滴3 小时前
Linux 高并发基石:epoll 核心原理 + LT/ET 触发模式深度剖析
linux·运维·服务器·开发语言·c++·网络协议