7、传输层协议 TC

TCP 协议

TCP 全称为 "传输控制协议(Transmission Control Protocol"). 人如其名, 要对数据的传 输进行一个详细的控制;

TCP 协议段格式

• 源/目的端口号: 表示数据是从哪个进程来, 到哪个进程去;

• 32 位序号/32 位确认号: 后面详细讲;

• 4 位 TCP 报头长度: 表示该 TCP 头部有多少个 32 位 bit(有多少个 4 字节); 所以 TCP 头部最大长度是 15 * 4 = 60

• 6 位标志位:

○ URG: 紧急指针是否有效

○ ACK: 确认号是否有效

○ PSH: 提示接收端应用程序立刻从 TCP 缓冲区把数据读走

○ RST: 对方要求重新建立连接; 我们把携带 RST 标识的称为复位报文段

○ SYN: 请求建立连接; 我们把携带 SYN 标识的称为同步报文段

○ FIN: 通知对方, 本端要关闭了, 我们称携带 FIN标识的为结束报文段

• 16 位窗口大小: 后面再说

• 16 位校验和: 发送端填充, CRC 校验. 接收端校验不通过, 则认为数据有问题. 此 处的检验和不光包含 TCP 首部, 也包含 TCP 数据部分.

• 16 位紧急指针: 标识哪部分数据是紧急数据;

• 40 字节头部选项: 暂时忽略

TCP 首部格式详解

TCP 首部长度最小为 20 字节 (没有选项时),最大为 60 字节(选项占 40 字节)。下图是每个字段的位置:

text

复制代码
 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
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|         源端口          |         目的端口           |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|                        序号 (Sequence Number)                  |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|                      确认序号 (Acknowledgment Number)          |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| 首部长度 | 保留 |U|A|P|R|S|F|           窗口大小           |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|         校验和           |         紧急指针           |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|                   选项 (可选)                 |    填充    |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|                            数据                              |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+

1. 源端口(16 位)& 目的端口(16 位)

  • 作用 :标识发送方和接收方的应用进程(通过端口号找到对应的 socket)。

  • 细节:端口号加上 IP 首部中的源 IP 和目的 IP,才能唯一确定一个 TCP 连接(四元组)。

  • 常用范围:0~1023 为系统保留(知名端口),1024~49151 为注册端口,49152~65535 为动态/私有端口。


2. 序号(32 位,Sequence Number)

  • 作用 :该 TCP 报文段中第一个数据字节的编号

  • 初始序号(ISN):建立连接时双方随机生成(避免历史报文干扰),而非固定从 1 开始。

  • 范围:0 ~ 2^32 - 1,到达最大值后回绕到 0(TCP 通过时间戳或扩展选项处理回绕问题)。

  • 为什么重要:保证数据有序、去重、检测丢失。


3. 确认序号(32 位,Acknowledgment Number)

  • 作用期望收到的下一个字节的序号,同时隐含确认了该序号之前的所有字节都已正确收到。

  • 有效条件 :仅当 ACK 标志位 = 1 时,确认序号字段才有效。

  • 累计确认:Ack = N 表示序号 N-1 及之前的数据都已收到。

  • 示例:收到 Seq=1001 且长度=500 的数据后,回复 Ack=1501(表示期待第 1501 字节)。


4. 首部长度(4 位)

  • 作用 :表示 TCP 首部有多少个 32 位字(4 字节)

  • 取值范围:5 ~ 15(因为最小 20 字节,即 5×4=20;最大 60 字节,即 15×4=60)。

  • 为什么需要:因为选项字段长度可变,接收方需要知道首部在哪里结束、数据从哪里开始。


5. 保留(6 位)

  • 作用:保留为未来使用,当前必须置 0。

6. 标志位(每个 1 位,共 6 位)

标志 名称 为 1 时的含义
URG Urgent 紧急指针字段有效,报文中有紧急数据(应优先处理)
ACK Acknowledgment 确认序号字段有效(除初始 SYN 报文外,几乎所有报文都置 1)
PSH Push 提示接收端立即将数据交给应用层,不要等缓冲区满
RST Reset 连接出现严重异常,需要强制关闭并重新建立连接(拒绝非法请求)
SYN Synchronize 建立连接时使用:SYN=1 表示这是一个连接请求或连接接受报文
FIN Finish 关闭连接时使用:发送方不再发送数据

补充细节

  • PSH 的实际行为:发送方设置 PSH 后,接收方 TCP 不会等待缓冲区填满,而是立即把数据递交给应用进程(但现代 TCP 实现通常自动优化,很少显式依赖)。

  • RST 常见场景:尝试连接一个未监听的端口、连接超时、收到不属于现存连接的报文。

  • SYN+ACK:服务器回复连接请求时,SYN=1, ACK=1。


7. 窗口大小(16 位)

  • 作用 :告诉对方从确认序号开始,自己还能接收多少字节的数据(即接收窗口大小)。

  • 范围 :0 ~ 65535 字节。若需要使用更大窗口,可以通过 TCP 窗口缩放选项(Window Scale)将窗口值左移若干位(最大可达 1GB)。

  • 用途 :实现流量控制,防止发送方发送过快导致接收方缓冲区溢出。

  • 动态变化:接收方根据自身可用缓冲区大小随时调整窗口值并通知发送方。


8. 校验和(16 位)

  • 作用:检测 TCP 首部和数据在传输过程中是否出现比特错误。

  • 计算范围TCP 伪首部(12 字节,包含源 IP、目的 IP、协议号、TCP 长度)+ TCP 首部 + TCP 数据。

    • 伪首部并不真正传输,只在计算校验和时临时构造。
  • 计算方式:将上述所有内容按 16 位字累加,进位回卷,最后取反码。

  • 接收方:同样计算,若结果不为全 1(即 0xFFFF)则丢弃报文。

为什么包含伪首部:为了验证报文是否确实发送给了正确的 IP 和协议(防止 IP 欺骗或路由错误)。


9. 紧急指针(16 位)

  • 作用 :仅在 URG=1 时有效,指向紧急数据的最后一个字节的序号(偏移量,相对于当前序号字段的值)。

  • 用途:发送紧急数据(如中断命令)时,接收方可以立即读取,而不被流控阻塞。

  • 实际使用:现代应用很少依赖,因为带外数据(out-of-band data)在其他机制下可能更复杂。


10. 选项(长度可变,最多 40 字节)

  • 常见选项

    • MSS (Maximum Segment Size):告诉对方自己能接收的最大报文段长度(不包括 TCP 首部)。

    • 窗口缩放因子:用于扩展窗口大小(Windows Scale)。

    • 时间戳:用于计算 RTT 和防止序号回绕(PAWS)。

    • SACK (Selective Acknowledgment):允许接收方告知哪些数据块丢失(提高重传效率)。

  • 填充:确保首部长度是 4 字节的整数倍(用 0 填充)。


总结:各字段的功能分组

功能 相关字段
标识进程 源端口、目的端口
可靠传输(有序/确认) 序号、确认序号、ACK、SYN、FIN
流量控制 窗口大小
错误检测 校验和
紧急数据 URG、紧急指针
连接控制 SYN、FIN、RST
提示接收方 PSH
扩展功能 选项、保留、首部长度

确认应答(ACK)机制

  • 主机A:发送方

  • 主机B:接收方

  • 数据(1~1000) :表示该TCP报文段携带的数据字节序号是从 1 到 1000。

  • 确认应答(下一个是1001):主机B收到数据后回复的确认报文,含义是 "我已收到 1~1000 的所有数据,下一个期望收到的字节序号是 1001"。

  • 数据(1001~2000):主机A收到确认后,继续发送下一个数据段,字节序号从 1001 到 2000。

  • 确认应答(下一个是2001):主机B再次回复,表示成功收到 1001~2000,期待序号 2001。

(1)序列号与累计确认
  • TCP 把数据流看作一个字节流,每个字节都有一个唯一的序列号(Seq)。

  • 主机A发送的第一个数据段"1~1000"实际上是指该数据段起始字节序号为 1,长度为 1000 字节。

  • 主机B回复的确认号(Ack)是 1001 ,这是 TCP 的 累计确认 机制:告诉主机A "我已经收到了序号 1000 及之前的所有字节,请从 1001 开始发送"。

(2)确认应答保证可靠
  • 主机A每发一个数据段,都需要收到来自主机B的确认。

  • 如果一段时间内没有收到确认(超时),主机A会重传未确认的数据。

  • 图中每个数据段都得到了确认,因此主机A可以正常发送下一段。

(3)发送与接收的同步
  • 主机A不会一次性把所有数据全部发出去,而是等待对方确认后再发下一段(这是最简单的 停-等协议 的体现)。

  • 在实际 TCP 中,为了提高效率,会使用 滑动窗口 允许连续发送多个数据段再等待确认,但图里展示的是基础逻辑:发送 → 确认 → 再发送。

TCP 将每个字节的数据都进行了编号. 即为序列号.

每一个 ACK 都带有对应的确认序列号, 意思是告诉发送者, 我已经收到了哪些数据; 下 一次你从哪里开始发

超时重传机制

• 主机 A 发送数据给 B 之后, 可能因为网络拥堵等原因, 数据无法到达主机 B;

• 如果主机 A 在一个特定时间间隔内没有收到 B 发来的确认应答, 就会进行重发

但是, 主机 A 未收到 B 发来的确认应答, 也可能是因为 ACK 丢失了;

因此主机 B 会收到很多重复数据. 那么 TCP 协议需要能够识别出那些包是重复的包, 并 且把重复的丢弃掉. 这时候我们可以利用前面提到的序列号, 就可以很容易做到去重的效果.

那么, 如何超时的时间如何确定?

• 最理想的情况下, 找到一个最小的时间, 保证 "确认应答一定能在这个时间内返 回".

• 但是这个时间的长短, 随着网络环境的不同, 是有差异的.

• 如果超时时间设的太长, 会影响整体的重传效率;

• 如果超时时间设的太短, 有可能会频繁发送重复的包

TCP 为了保证无论在任何环境下都能比较高性能的通信, 因此会动态计算这个最大超 时时间

• Linux 中(BSD Unix 和 Windows 也是如此), 超时以 500ms 为一个单位进行控 制, 每次判定超时重发的超时时间都是 500ms 的整数倍.

• 如果重发一次之后, 仍然得不到应答, 等待 2*500ms 后再进行重传.

• 如果仍然得不到应答, 等待 4*500ms 进行重传. 依次类推, 以指数形式递增.

• 累计到一定的重传次数, TCP 认为网络或者对端主机出现异常, 强制关闭连接

连接管理机制

在正常情况下, TCP 要经过三次握手建立连接, 四次挥手断开连接

三次握手(建立连接)

当前状态 事件 / 动作 下一状态 说明
CLOSED 服务器执行 listen() LISTEN 服务器被动监听
CLOSED 客户端执行 connect() SYN_SENT 客户端发送 SYN
LISTEN 收到客户端的 SYN SYN_RCVD 服务器回复 SYN+ACK
SYN_SENT 收到服务器的 SYN+ACK ESTABLISHED 客户端回复 ACK
SYN_RCVD 收到客户端的 ACK ESTABLISHED 连接建立完成

四次挥手(关闭连接)

关键原则 :主动调用 close() 的一方为主动关闭方 ,另一方为被动关闭方。下面分别列出两种场景。

场景 A:客户端主动关闭(服务器被动关闭)
角色 当前状态 事件 / 动作 下一状态 说明
客户端(主动) ESTABLISHED 调用 close(),发送 FIN FIN_WAIT_1 主动发起关闭
服务器(被动) ESTABLISHED 收到 FIN,回复 ACK CLOSE_WAIT 应用层会收到 EOF
客户端 FIN_WAIT_1 收到 ACK(对 FIN 的确认) FIN_WAIT_2 等待服务器发送 FIN
服务器 CLOSE_WAIT 应用层处理完数据后调用 close(),发送 FIN LAST_ACK 主动发送 FIN
客户端 FIN_WAIT_2 收到服务器的 FIN,回复 ACK TIME_WAIT 进入 2MSL 等待
服务器 LAST_ACK 收到客户端对 FIN 的 ACK CLOSED 彻底关闭
客户端 TIME_WAIT 等待 2MSL 后 CLOSED 防止残留报文干扰
场景 B:服务器主动关闭(客户端被动关闭)
角色 当前状态 事件 / 动作 下一状态 说明
服务器(主动) ESTABLISHED 调用 close(),发送 FIN FIN_WAIT_1 主动发起关闭
客户端(被动) ESTABLISHED 收到 FIN,回复 ACK CLOSE_WAIT 应用层会收到 EOF
服务器 FIN_WAIT_1 收到 ACK(对 FIN 的确认) FIN_WAIT_2 等待客户端发送 FIN
客户端 CLOSE_WAIT 应用层处理完数据后调用 close(),发送 FIN LAST_ACK 主动发送 FIN
服务器 FIN_WAIT_2 收到客户端的 FIN,回复 ACK TIME_WAIT 进入 2MSL 等待
客户端 LAST_ACK 收到服务器对 FIN 的 ACK CLOSED 彻底关闭
服务器 TIME_WAIT 等待 2MSL 后 CLOSED 防止残留报文干扰

注意TIME_WAIT 只会出现在主动关闭方CLOSE_WAITLAST_ACK 出现在被动关闭方。

常见状态解释

状态 含义 出现位置
LISTEN 服务器监听,等待客户端连接 服务器
SYN_SENT 客户端已发送 SYN,等待 SYN+ACK 客户端
SYN_RCVD 服务器收到 SYN,已回复 SYN+ACK,等待 ACK 服务器
ESTABLISHED 连接已建立,可以传输数据 双方
FIN_WAIT_1 主动关闭方已发送 FIN,等待 ACK 主动方
FIN_WAIT_2 主动关闭方已收到 FIN 的 ACK,等待对方 FIN 主动方
CLOSE_WAIT 被动关闭方收到 FIN,等待应用层关闭 被动方
LAST_ACK 被动关闭方已发送 FIN,等待最终 ACK 被动方
TIME_WAIT 主动关闭方收到 FIN 并回复 ACK,等待 2MSL 主动方
CLOSED 连接完全关闭 双方

补充:

  1. TIME_WAIT 为什么要等 2MSL?

    • 保证主动关闭方发送的最后一个 ACK 能到达对方(若丢失,对方重发 FIN,自己还能响应)。

    • 让本次连接的所有残留报文在网络中消失,避免干扰新连接。

  2. CLOSE_WAIT 容易出问题

    • 如果被动关闭方的应用层没有及时调用 close(),连接会一直卡在 CLOSE_WAIT,导致文件描述符泄漏。

详细解释:

一、前期准备(服务器端先启动)

1. 服务器端应用层
  • listenfd = socket():创建一个 监听 socket ,返回文件描述符 listenfd

  • bind(listenfd, 服务器地址端口):将 listenfd 绑定到服务器的 IP 和端口(例如 0.0.0.0:8080)。

  • listen(listenfd, 连接队列长度):将 listenfd 变为被动监听状态,内核为该 socket 维护一个已完成连接队列(backlog)。

2. 服务器端 TCP 层状态
  • 执行 listen() 后,服务器 TCP 状态从 CLOSEDLISTEN,等待客户端连接。

此时服务器阻塞在 accept() 调用上,等待客户端连接到来。


二、TCP 三次握手(建立连接)

1. 客户端应用层
  • fd = socket():创建主动 socket。

  • connect(fd, 服务器地址端口):发起连接请求,阻塞等待服务器应答。

2. 客户端 TCP 层状态
  • 调用 connect() 后,客户端状态:CLOSEDSYN_SENT(发送 SYN 报文)。
3. 服务器端 TCP 层
  • 收到 SYN 后,状态:LISTENSYN_RCVD,并回复 SYN+ACK。

  • 客户端收到 SYN+ACK 后,状态:SYN_SENTESTABLISHED,并回复 ACK。

  • 服务器收到 ACK 后,状态:SYN_RCVDESTABLISHED

4. 应用层返回
  • 客户端 connect() 返回,表示连接建立成功。

  • 服务器端阻塞的 accept() 返回,生成一个新的已连接 socket connfd,用于与该客户端通信。

注:accept() 返回后,服务器 TCP 层已经处于 ESTABLISHED 状态。


三、数据传输(可循环多次)

服务器端应用层:
  1. read(connfd, buf, size):阻塞等待客户端请求数据。

  2. 收到数据后 read 返回,处理请求

  3. write(connfd, buf, size):向客户端发送应答数据。

  4. 继续循环 read → 处理 → write,形成多次请求-应答(图中所示"循环多次")。

客户端应用层:
  • 图中未画客户端的数据发送,但通常客户端也会 write 发送请求,read 接收应答。
TCP 层状态:
  • 整个数据传输期间,双方 TCP 状态保持在 ESTABLISHED

  • 每次 write 产生数据报文,收到 ACK 确认;每次 read 获取对方发来的数据。

图中客户端 TCP 层状态一栏写着 DATAACK,这并非正式状态,而是表示数据发送和确认阶段。


四、TCP 四次挥手(关闭连接)

以服务器端主动关闭为例(服务器调用 close(connfd))。

1. 服务器端应用层调用 close(connfd)
  • TCP 层发送 FIN 报文,主动关闭
2. 服务器端 TCP 状态转移
  • ESTABLISHEDFIN_WAIT_1(发送 FIN 后)。

  • 收到客户端对 FIN 的 ACK 后:FIN_WAIT_1FIN_WAIT_2

  • 收到客户端的 FIN 后:发送 ACK,FIN_WAIT_2TIME_WAIT(等待 2MSL 后关闭)。

  • TIME_WAITCLOSED

3. 客户端 TCP 状态转移(被动关闭)
  • 收到 FIN 后:发送 ACK,ESTABLISHEDCLOSE_WAIT (图中未显示,但标准是 CLOSE_WAIT)。

  • 客户端应用层调用 close() 后:发送 FIN,状态 CLOSE_WAITLAST_ACK

  • 收到服务器对 FIN 的 ACK 后:LAST_ACKCLOSED

图中客户端 TCP 层状态只画到了 FIN_WAIT_2?实际上对客户端来说,被动关闭时没有 FIN_WAIT_1/2,只有 CLOSE_WAITLAST_ACK。图中可能简化了,或者画的是客户端主动关闭的情况。建议按标准理解。


五、"循环多次"的含义

  • 服务器端 :在 accept() 得到 connfd 后,在一个循环中反复 read/write,处理同一条连接上的多次请求(例如 HTTP 持久连接)。

  • 外层循环accept() 本身可以循环,接受多个客户端的连接,每个连接分配一个 connfd,分别处理。

在服务器端标注了两个"循环多次":

  • 内层:对同一个 connfd 反复读请求 → 发应答。

  • 外层:不断 accept() 新连接。


六、关键总结

系统调用 作用 对应 TCP 状态变化
socket() 创建套接字 不改变状态
bind() 绑定地址端口 无状态变化
listen() 变为监听套接字 CLOSEDLISTEN
accept() 接受连接 返回已连接套接字,状态不变
connect() 主动连接 CLOSEDSYN_SENTESTABLISHED
read()/write() 读写数据 保持 ESTABLISHED
close() 关闭连接 触发四次挥手,状态变化如上

TCP 状态转换的汇总

• 较粗的虚线表示服务端的状态变化情况;

• 较粗的实线表示客户端的状态变化情况;

• CLOSED 是一个假想的起始点, 不是真实状态;

相关推荐
田里的水稻1 小时前
FA_IPC_协议网络(GRPC)数据交互三
网络·人工智能·机器人
小哇6661 小时前
MCP服务 SSE / Streamable HTTP 这两种数据传输机制,怎么用 http 请求查询这个MCP服务支持哪些工具调用, 和怎么调用其中一个工具
网络·网络协议·http
艾莉丝努力练剑1 小时前
【Qt】事件
服务器·开发语言·网络·数据库·qt·tcp/ip·计算机网络
weixin_604236671 小时前
华为企业级路由器完整版实战配置
网络·安全·华为·智能路由器·华为交换机命令·华为路由器
洛水水1 小时前
图床项目实现:注册登录 + 文件上传等功能的完善
网络·c++·mysql·图床
呉師傅1 小时前
联想M7400Pro提示无法打印0B 关闭电源然后重新打开故障维修分享
运维·网络·windows·电脑
名不经传的养虾人1 小时前
从0到1:企业级AI项目迭代日记 Vol.41|多租户不是一个功能,是一次手术
服务器·数据库·系统架构·ai编程·ai工作流·企业ai
zbtlink1 小时前
买路由器,到底是在买什么?
网络·智能路由器
ai_xiaogui1 小时前
PanelAI 是什么?服务器上 ComfyUI、OpenClaw、Stable Diffusion 一键部署神器,普通开发者也能轻松管理 AI 项目
服务器·人工智能·stable diffusion