【Java EE】TCP—可靠传输

可靠传输

可靠传输

确认应答机制

为什么需要序号?

TCP 将应用层的数据流切分成多个段(Segment)。每个段携带的载荷(Payload)都是一段连续的字节。为了让接收方能够正确重组数据,TCP 为每个字节 都分配了一个唯一的序号(Sequence Number)。段头中的 32 位序号字段 填写的是该段载荷第一个字节的序号。

字段 大小 说明
序号 4 字节 本报文段数据的第一个字节的编号
确认号 4 字节 期望收到对方下一个报文段的第一个字节序号

例如,第一个段携带字节 1~1000,它的序号就是 1;第二个段携带字节 1001~2000,序号就是 1001。序号在整个连接生命周期中单调递增(超出 32 位范围后回绕)。

注意:TCP 报头本身不参与编号,序号和确认序号都是针对载荷数据的。

确认序号的含义

当接收方收到数据后,会发送一个确认应答段(ACK),其中的确认序号 填写的是收到的数据载荷的最后一个字节序号 + 1。

例如:主机 A 发送了序号为 1、长度为 1000 字节的数据(字节 1~1000)。主机 B 收到后,确认序号应为 1000 + 1 = 1001。

这个确认序号向发送方传达了双重含义:

  1. 所有序号小于 1001 的数据都已经确认收到了(即字节 1~1000 已安全抵达);
  2. 接下来请你从序号 1001 开始发送数据。

主机B(接收方) 主机A(发送方) 主机B(接收方) 主机A(发送方) 收到 1~1000 收到 ACK,知道 1~1000 已确认 下次从 1001 开始发送 数据段(序号=1,载荷 1~1000) 确认应答(确认序号=1001)

处理后发先至与数据排序

由于 IP 网络的路由具有不确定性,同一个 TCP 连接上的不同段可能 后发先至(先发出的段反而晚到)。如果 TCP 不处理,应用层可能读到乱序的数据。

TCP 的解决方案是在接收方内核中维护一个接收缓冲区:

  • 所有通过网卡收到的 TCP 段,先存入接收缓冲区;
  • 接收方根据序号对缓冲区中的段进行排序,乱序到达的段会被重新排列成正确的顺序;
  • 应用程序调用 read() 时,从缓冲区中按顺序读取数据。

因此,使用 TCP 的开发者完全不必担心数据顺序问题------TCP 已经默默处理了所有乱序。
接收方
网络
发送方
write
段2先到
段1后到
按序号排序
read
应用数据流
拆分为段

序号1,2,3...
接收缓冲区
有序队列
应用程序得到

正确顺序的数据

接收缓冲区:生产者消费者模型

接收缓冲区不仅用于排序,还起到了流量控制的作用。它可以看作一个典型的生产者-消费者模型:

  • 生产者:网卡驱动从网络中读取 TCP 段,放入缓冲区;
  • 消费者 :应用程序调用 read() 从缓冲区取走数据。

如果应用程序读取速度慢于数据到达速度,缓冲区会逐渐填满,此时接收方会通过窗口大小字段告知发送方暂停或减慢发送,防止数据被丢弃。
网卡接收
read系统调用
处理完数据
网络到达的TCP段
内核接收缓冲区
应用程序

超时重传机制

RTO的基本原理

发送方每发送一个数据段,都会启动一个重传定时器 ,超时时间记为 RTO (Retransmission Timeout)。如果在 RTO 内没有收到对应的 ACK,发送方就认为该段已丢失,于是重新发送该数据段。

丢包可能发生在两种场景:

  1. 发送的数据段本身丢失(例如路由器丢弃);
  2. 接收方返回的 ACK 丢失(发送方同样无法收到确认,误以为数据丢失)。

无论哪种情况,超时重传都能促使发送方重发数据,保证最终抵达。
接收方 发送方 接收方 发送方 启动重传定时器 RTO 定时器超时,未收到 ACK 重启定时器(RTO 可能翻倍) loop 每次超时且未达上限 收到确认,停止定时器,传输完成 发送数据段 (seq=1...1000) 重传数据段 (seq=1...1000) ACK (ack=1001)

RTO 的动态调整与指数退避

RTO 不是固定值,而是根据网络往返时间(RTT)动态计算出来的。简单说,TCP 会持续测量每个报文段的 RTT,使用平滑的 SRTT 和偏差 RTTVAR 计算:

text 复制代码
RTO = SRTT + 4 × RTTVAR

初始 RTO 通常在 200ms~1s 之间。

超时重传发生时,TCP 会认为网络可能已经拥塞,继续用相同间隔重传只会加重拥塞。

于是采用指数退避 :每次超时后将 RTO 翻倍,直到收到 ACK 或放弃连接。
收到ACK
超时
否
是
发送数据段, 启动定时器

RTO = 初始值
等待ACK
完成
重传数据段
重传次数

已达上限?
RTO = RTO * 2

重启定时器
放弃传输, 断开连接

TCP 不会无限重传。通常有两个条件之一满足即放弃:

  • 重传次数达到上限(Linux 中 tcp_retries2 默认 15);
  • 累计超时等待时间超过绝对上限(通常几分钟)。

一旦放弃,TCP 会断开连接并通知应用程序(例如返回"连接超时"错误)。

RTO带来的重复数据与去重机制

当 ACK 丢失 时,发送方会误认为数据段丢失而重传,但数据实际上已被接收方正确收到,于是接收方会第二次收到完全相同的数据段。
接收方 发送方 接收方 发送方 正确收到,放入接收缓冲区 定时器超时,未收到 ACK 认为数据段丢失 发现重复! 检查缓冲区已有 1...1000 收到 ACK,停止重传 数据段 (seq=1...1000) ACK (ack=1001) ❌ 丢失 重传数据段 (seq=1...1000) 丢弃重复数据段 再次发送 ACK (ack=1001)

如果 TCP 不处理,应用程序会读到两次相同的数据。对于扣款、文件写入等操作,这会造成严重错误。因此 TCP 必须在接收方进行去重。

接收缓冲区如何实现去重?

接收方通过接收缓冲区 + 序号检查做到完美去重:

  • 每个 TCP 段都有一个起始序号 和长度。

  • 接收方内核维护一个已收到的连续字节流的缓冲区。

  • 当收到新的 TCP 段时,检查其序号区间是否已经存在于缓冲区:

    • 已存在 → 直接丢弃,不通知应用程序;
    • 不存在 → 插入缓冲区合适位置,可能触发乱序重组。

已存在
不存在
收到 TCP 段

序号 = Seq, 长度 = Len
在接收缓冲区中

查找序号区间 Seq, Seq+Len-1
丢弃该段, 不通知应用
插入缓冲区适当位置

可能触发排序/合并
发送 ACK 确认
应用程序后续读取到

唯一且有序的数据

注意 :即使重复段被丢弃,接收方也必须再次发送 ACK,否则发送方可能继续无意义的重传。

相关推荐
通信大模型10 小时前
IEEE TCCN | 面向低空经济网络的Agentic AI驱动多无人机轨迹优化
网络·人工智能·无人机
wdfk_prog10 小时前
LWIP教程 09:从 `tcp_slowtmr()` 到 Fast Retransmit——TCP 超时重传与重复 ACK
服务器·网络·数据库·网络协议·tcp/ip
Zooproxy全球IP代理10 小时前
住宅IP在AI行业中的技术应用:数据采集基础设施的工程实践
人工智能·网络协议·tcp/ip
15Moonlight10 小时前
Linux网络(02):Socket 编程 UDP
linux·网络·udp
盛世宏博智慧档案10 小时前
一文读懂 PoE+Modbus TCP 温湿度传感器布线规范与故障排查
网络·网络协议·tcp/ip
liulilittle11 小时前
Linux 下 select 测试函数
linux·服务器·网络·数据库·c++·select·c
夜雪一千11 小时前
Python 生成模拟地址数据:Faker之外的备选库
网络·windows·python
骑着蜗牛撵大象32711 小时前
服务下线不再炸:分层关闭、TCP 存活探测与负载均衡排水的落地套路
android·tcp/ip·负载均衡·tcp·高可用·健康检查·优雅关闭
小此方11 小时前
Linux网络(二十一):深入理解 TCP 异常处理:网线断开、Keepalive 保活机制与 Linux 内核传输层协议源码剖析
linux·网络·tcp/ip