【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,否则发送方可能继续无意义的重传。

相关推荐
沃虎电子5 小时前
片式网络变压器:从“手工品”到“SMD元件”的产业跨越
网络·片式网络变压器
神奇小梵5 小时前
关于finalshell的使用
linux·服务器·网络
上海云盾-小余6 小时前
恶意爬虫精准拦截:网站流量净化与资源守护方案
网络·爬虫·web安全
dog2506 小时前
解析几何的现代范式-算力,拟合与对偶
服务器·开发语言·网络·线性代数·php
dangdanding6 小时前
防火墙 IP 分片测试套件-fragroute
linux·网络·网络协议·tcp/ip
happymade6 小时前
全网拓扑自动发现与服务器全维度监控的技术实践
linux·运维·服务器·网络·zabbix·路由器·prometheus
TechWayfarer6 小时前
AI大模型时代:IP数据云如何适配智能体场景需求
开发语言·人工智能·python·网络协议·tcp/ip·langchain
small_white_robot7 小时前
(Win)文件上传数据流绕过-面试常考
网络·安全·web安全·网络安全
cui_ruicheng7 小时前
Linux网络编程(五):基于UDP实现DictServer
linux·服务器·网络·udp