当前位置: 首页 > news >正文

【Linux网络】深入理解 TCP 协议(一):报头设计与可靠性基石

🔥草莓熊Lotso:个人主页

❄️个人专栏:《C++知识分享》 《Linux 入门到实践:零基础也能懂》

✨生活是默默的坚持,毅力是永久的享受!

🎬 博主简介:


文章目录

  • 前言:
  • 一. TCP 的简单回顾
    • 1.1 TCP 在网络分层中的位置
    • 1.2 TCP 数据发送的本质
  • 二. TCP 协议格式深度解析
    • 2.1 TCP 报头整体结构
    • 2.2 核心字段详解
      • 2.2.1 源 / 目的端口号(16 位)
      • 2.2.2 32 位序号与确认序号
      • 2.2.3 4 位首部长度(重点)
      • 2.2.4 6 位标志位
    • 2.3 Linux 内核中 TCP 报头的实现
    • 2.4 TCP 与 UDP 报头的关键区别
  • 三. TCP 可靠性核心:确认应答与超时重传
    • 3.1 可靠性的本质
    • 3.2 确认应答(ACK)机制
    • 3.3 超时重传机制
  • 结尾:

前言:

在互联网世界中,TCP 协议无疑是最核心的基石之一 —— 我们每天使用的 HTTP、HTTPS、SSH、FTP 等几乎所有可靠通信都建立在 TCP 之上。很多人知道 TCP 是 “可靠的传输控制协议”,但很少有人深入理解它的可靠性究竟是如何实现的,以及它的报头设计背后隐藏着怎样的精妙考量。本文将从最基础的 TCP 数据发送流程讲起,深度拆解 TCP 报头的每一个字段,结合 Linux 内核源码分析其底层实现,并详细讲解 TCP 可靠性的两大核心机制:确认应答与超时重传。所有内容均严格基于 TCP 协议规范和 Linux 内核实现,力求做到理论与实践相结合。


一. TCP 的简单回顾

1.1 TCP 在网络分层中的位置

我们先回顾一下经典的网络分层模型,明确 TCP 的定位:

OSI 参考模型TCP/IP 分层模型典型协议
应用层应用层HTTP、HTTPS、SSH、FTP
表示层--
会话层--
传输层传输层TCP、UDP、SCTP
网络层互联网层IP、ICMP、ARP
数据链路层网卡层以太网协议
物理层(硬件)-


TCP 位于传输层,负责在两台主机的进程之间提供可靠的、面向连接的、字节流的通信服务。

1.2 TCP 数据发送的本质

很多初学者会误以为调用write/send函数就是直接把数据发送到网络上,这是一个常见的误区。实际上,我们向网络发送数据的本质是将数据拷贝到操作系统内核的 TCP 发送缓冲区中。

应用程序 write()→ 拷贝数据到TCP发送缓冲区 → TCP协议栈控制发送时机、速率 → 网络

TCP 协议栈完全自主决定:

  • 发多少数据
  • 什么时候发
  • 以什么速率发

这就是 “传输控制协议” 名称的由来。同时,TCP 必须维护接收缓冲区,用于存放收到的数据,等待应用层读取。

正是因为有了发送和接收缓冲区,TCP 才能够实现面向字节流的特性 —— 数据被看作是一连串无结构的字节流,传输层不关心应用层的报文边界。


二. TCP 协议格式深度解析

TCP 报头是 TCP 协议的核心,所有的控制信息都包含在报头中。理解 TCP 报头是掌握 TCP 协议的基础。

2.1 TCP 报头整体结构

TCP 报头由固定 20 字节的标准部分最多 40 字节的可选部分组成,总长度范围为 20~60 字节。

字段长度字段名称核心作用
16 位源端口号标识发送方进程
16 位目的端口号标识接收方进程
32 位序号本报文段第一个数据字节的编号
32 位确认序号期望收到对方下一个字节的编号
4 位首部长度以 4 字节为单位的 TCP 首部总长度
6 位保留位预留为将来扩展,必须置 0
6 位标志位控制 TCP 连接状态和数据传输
16 位窗口大小接收方的接收能力(流量控制)
16 位检验和校验 TCP 首部和数据的完整性
16 位紧急指针标识紧急数据的末尾位置
0~40 字节选项扩展 TCP 功能(如 MSS、窗口扩大因子)
可变长度数据应用层有效载荷

2.2 核心字段详解

2.2.1 源 / 目的端口号(16 位)

这两个字段解决了有效载荷的分用问题—— 即数据应该交付给哪个进程。

端口号范围是 0~65535,其中:

  • 0~1023:知名端口,分配给标准服务(如 HTTP=80,HTTPS=443,SSH=22)
  • 1024~49151:注册端口,供用户进程使用
  • 49152~65535:动态端口,由操作系统自动分配

2.2.2 32 位序号与确认序号

这两个字段是 TCP 可靠性的基础。TCP 将每个字节的数据都进行了编号,序号就是本报文段中第一个数据字节的编号。

确认序号则表示:我已经收到了确认序号之前的所有字节,下一次请从确认序号开始发送。

例如:A 发送了序号为 1~1000 的字节数据,B 收到后会回复确认序号为 1001 的 ACK 报文。

2.2.3 4 位首部长度(重点)

这是 TCP 报头设计中最精妙的字段之一,也是很多面试的高频考点。

  • 为什么需要这个字段?

    • TCP 报头包含可变长度的选项部分,因此接收方无法预先知道报头的总长度。4 位首部长度字段就是用来告诉接收方,TCP 报头到底有多长。
  • 字段规则

    • 4 位二进制范围:0000~1111(十进制 0~15)
    • 基本单位:4 字节
    • 实际首部长度 = 字段值 × 4 字节
  • 取值范围

    • 最小值:5(5×4=20 字节),表示没有选项的标准报头
    • 最大值:15(15×4=60 字节),表示选项部分占满 40 字节
  • 设计精髓:4 字节对齐

    • 为什么要以 4 字节为单位?因为 4 位最多只能表示 15 个值,直接表示字节长度的话最多只能到 15 字节,远远不够。通过规定基本单位为 4 字节,将表达范围从 0~15 字节扩展到了 0~60 字节。
    • 更深层次的原因是:TCP 报头长度必须是 4 字节的整数倍,因此报头长度的二进制表示的低两位永远是 0。我们不需要存储这两位,只需要存储高 4 位即可,读取时再左移两位(×4)补回低两位的 0。
  • 计算示例

    • 标准报头 20 字节:20 ÷ 4 = 5 → 字段值为 5(二进制 0101)
    • 带 12 字节选项的报头:20+12=32 字节 → 32 ÷ 4 = 8 → 字段值为 8(二进制 1000)

2.2.4 6 位标志位

这 6 个标志位用于控制 TCP 连接的状态和数据传输方式:

  • URG:紧急指针有效
  • ACK:确认序号有效(所有数据传输阶段的报文都必须置 1)
  • PSH:提示接收端立即将数据从 TCP 缓冲区推送给应用层
  • RST:强制重置连接
  • SYN:请求建立连接
  • FIN:请求关闭连接

2.3 Linux 内核中 TCP 报头的实现

我们来看一下 Linux 内核中tcphdr结构体的定义(位于include/linux/tcp.h),这是 TCP 报头在代码中的直接映射:

// linux kernel include/linux/tcp.hstructtcphdr{__be16 source;// 16位源端口号,网络字节序(大端)__be16 dest;// 16位目的端口号,网络字节序__be32 seq;// 32位序号__be32 ack_seq;// 32位确认序号#ifdefined(__LITTLE_ENDIAN_BITFIELD)__u16 res1:4,// 保留位4位doff:4,// 4位首部长度(数据偏移)fin:1,// FIN标志:关闭连接syn:1,// SYN标志:建立连接rst:1,// RST标志:重置连接psh:1,// PSH标志:推送数据ack:1,// ACK标志:确认号有效urg:1,// URG标志:紧急指针有效ece:1,// ECE标志:显式拥塞通知回显cwr:1;// CWR标志:拥塞窗口减小#elifdefined(__BIG_ENDIAN_BITFIELD)__u16 doff:4,// 大端模式下,位段顺序相反res1:4,cwr:1,ece:1,urg:1,ack:1,psh:1,rst:1,syn:1,fin:1;#else#error"Adjust your <asm/byteorder.h> defines"#endif__be16 window;// 16位窗口大小__sum16 check;// 16位检验和__be16 urg_ptr;// 16位紧急指针};

源码解读

  • 位段的使用:TCP 报头中有很多单个位的标志,使用 C 语言的位段特性可以节省内存,同时方便按位操作。
  • 大小端适配:由于不同 CPU 架构的字节序不同,内核通过条件编译来适配小端和大端模式下的位段顺序。
  • 网络字节序:所有多字节字段(如sourcedestseq等)都使用__be16__be32类型,表示网络字节序(大端)。

2.4 TCP 与 UDP 报头的关键区别

很多人会问:为什么 UDP 报头有 16 位长度字段,而 TCP 没有?

答案在于两者的服务模型不同:

  • UDP 是面向数据报的:每个 UDP 报文都是独立的、有边界的。长度字段告诉接收方这个 UDP 报文的总长度,从而可以准确分离报头和有效载荷。
  • TCP 是面向字节流的:TCP 将数据看作是连续的字节流,传输层不关心应用层的报文边界。TCP 只需要保证字节流可靠到达,报文的边界由应用层自己处理。

三. TCP 可靠性核心:确认应答与超时重传

TCP 最核心的特性就是可靠性。那么,TCP 是如何在不可靠的网络层之上实现可靠传输的呢?

3.1 可靠性的本质

首先我们要明确一个重要结论:网络世界中没有 100% 可靠的协议

这是一个经典的 “蓝军红军问题”:永远有最新的消息还没有得到应答,无法确认对方是否收到。但 TCP 通过确认应答机制,保证了被应答的历史数据 100% 可靠

3.2 确认应答(ACK)机制

TCP 可靠性的基础就是确认应答机制:A 主机发送给 B 主机的每一个数据段,B 主机都必须给 A 主机回复一个 ACK 确认报文

工作流程

  • A 发送数据段(序号 1~1000)给 B
  • B 收到数据后,回复 ACK 报文(确认序号 1001)
  • A 收到 ACK 后,就知道 1~1000 字节已经被 B 可靠接收
  • A 继续发送下一个数据段(序号 1001~2000)

两个关键细节

  • ACK 是由对方操作系统的 TCP 层自动回复的,不需要应用层参与。这保证了即使应用层很忙,也不会影响 TCP 的可靠性。
  • ACK 本身不需要被应答,否则会陷入无限循环。TCP 通过超时重传机制来处理 ACK 丢失的情况。


3.3 超时重传机制

如果 A 在一定时间内没有收到 B 的 ACK,会发生什么?

对于 A 来说,无法区分是以下哪种情况:

  • 数据本身在传输过程中丢失了,B 根本没有收到
  • B 收到了数据并回复了 ACK,但 ACK 在传输过程中丢失了

因此,TCP 统一按照 “数据丢失” 处理:只要在超时时间内没有收到 ACK,就重传对应的数据段

TCP 的去重机制:由于可能会重传数据,接收方可能会收到重复的报文段。TCP 通过序号来识别重复的报文段,并将重复的丢弃,保证应用层只会收到一次数据。

动态超时时间计算:TCP 的超时时间不是固定的,而是根据网络状况动态计算的:

  • Linux 系统以 500ms 为基本单位
  • 第一次超时等待 500ms
  • 如果重传后仍然没有收到 ACK,等待时间加倍(1000ms)
  • 以此类推,以指数形式递增
  • 累计重传一定次数后,TCP 认为网络或对端异常,强制关闭连接

结尾:

🍓 我是草莓熊 Lotso!若这篇技术干货帮你打通了学习中的卡点: 👀 【关注】跟我一起深耕技术领域,从基础到进阶,见证每一次成长 ❤️ 【点赞】让优质内容被更多人看见,让知识传递更有力量 ⭐ 【收藏】把核心知识点、实战技巧存好,需要时直接查、随时用 💬 【评论】分享你的经验或疑问(比如曾踩过的技术坑?),一起交流避坑 🗳️ 【投票】用你的选择助力社区内容方向,告诉大家哪个技术点最该重点拆解 技术之路难免有困惑,但同行的人会让前进更有方向~愿我们都能在自己专注的领域里,一步步靠近心中的技术目标!

结语:本文我们从 TCP 的基本工作流程讲起,深度解析了 TCP 报头的每一个字段,特别是 4 位首部长度的设计精髓,并结合 Linux 内核源码分析了其底层实现。同时,我们详细讲解了 TCP 可靠性的两大核心机制:确认应答和超时重传,理解了 TCP 如何在不可靠的网络之上实现可靠的数据传输。当然,TCP 的复杂之处远不止于此。为了提高传输性能,TCP 还引入了滑动窗口、快速重传、流量控制、拥塞控制等机制;为了管理连接,TCP 设计了三次握手和四次挥手的流程。这些内容我们将在后续的文章中逐一拆解。如果本文对你有帮助,欢迎点赞、收藏、关注,我会持续分享更多 Linux 系统编程和网络编程的干货内容。

✨把这些内容吃透超牛的!放松下吧✨
ʕ˘ᴥ˘ʔ
づきらど

http://www.zskr.cn/news/1508648.html

相关文章:

  • AI推广品牌哪家好,按年收费且性价比高的有哪些 - mypinpai
  • Plotly Express实战指南:三行代码构建交互式数据看板
  • 从“直通”到“炸管”:手把手分析一个MOS管驱动电路的失败案例
  • 创维E900V22D刷Armbian系统终极指南:从电视盒子到高性能服务器的完美蜕变
  • 别再让需求文档睡大觉了!用Aspice SWE.1的8个实践,盘活你的软件需求分析
  • 计算机毕业设计之艺术作品展示平台及版权保护机制
  • Spring Boot + PgVector 实现企业级 RAG 向量检索实战
  • Python图像预处理实战:OpenCV工业级噪声滤波与光照归一化
  • 告别混乱指示灯:手把手教你用NPEM(PCIe 4.0+)统一管理服务器SSD状态灯
  • Java写的局域网双人五子棋,带服务端和客户端完整可运行代码
  • 企业级火锅店管理系统管理系统源码|SpringBoot+Vue+MyBatis架构+MySQL数据库【完整版】
  • 秒杀场景下,为什么我放弃了线程池而选择了阻塞队列?聊聊异步处理的选型思考
  • 700万用户真实AI行为解密:从工具使用到认知协作的四阶跃迁
  • 2026年成都二手叉车市场深度观察:回收、售卖与租赁服务商综合评测 - 优质品牌商家
  • 【2027最新】基于SpringBoot+Vue的火锅店管理系统管理系统源码+MyBatis+MySQL
  • CTAP协议实战:用Python模拟一个FIDO2认证器,深入理解WebAuthn背后的握手过程
  • Windows下可直接运行的C++加壳工具集:含加壳主程序、Shell动态库与完整VS2013源码
  • 2026年洁净工程行业观察:净化车间设计施工公司综合能力对比分析 - 优质品牌商家
  • Vue Json Pretty 技术深度解析:现代Vue应用中的高性能JSON数据可视化解决方案
  • AUTOSAR CP LIN_Slave 从机协议栈设计与实现
  • 双流架构在商用车健康监测中的创新应用
  • 5分钟解锁全网音乐神器:LXMusic音源零基础小白也能上手的完整攻略
  • 2026年广州真丝面料采购指南:从源头工厂到技术工艺的深度解析 - 优质品牌商家
  • 2026成都工地空压机出租哪家强?6家实力企业深度横评与真实案例解析 - 优质品牌商家
  • 2026年山东成人高考机构怎么选?基于办学资质与教务服务的行业分析报告 - 优质品牌商家
  • 知识图谱在分布式智能决策中的架构设计与优化
  • 2026年成都法拍房机构口碑观察:哪些服务商值得关注? - 优质品牌商家
  • 告别RGB软件混乱:OpenRGB统一控制你的所有灯光设备
  • MLOps实战:构建可审计、可观测、可伸缩的生产级模型服务
  • Halcon 3D点云处理实战:用get_object_model_3d_params()提取关键特征,实现自动化尺寸测量