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


🔥草莓熊Lotso: 个人主页
❄️个人专栏: 《C++知识分享》 《Linux 入门到实践:零基础也能懂》
✨生活是默默的坚持,毅力是永久的享受!


🎬 博主简介:


文章目录

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

bash 复制代码
应用程序 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 报头在代码中的直接映射:

c 复制代码
// linux kernel include/linux/tcp.h
struct tcphdr {
    __be16 source;    // 16位源端口号,网络字节序(大端)
    __be16 dest;      // 16位目的端口号,网络字节序
    __be32 seq;       // 32位序号
    __be32 ack_seq;   // 32位确认序号
#if defined(__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标志:拥塞窗口减小
#elif defined(__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 认为网络或对端异常,强制关闭连接

结尾:

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

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

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

相关推荐
风曦Kisaki1 小时前
#Linux监控与安全Day02:Zabbix 自动发现,Zabbix 报警机制,Zabbix 主动监控,监控 Nginx 服务
linux·运维·nginx·安全·自动化·云计算·zabbix
kebidaixu1 小时前
Modbus TCP 协议详解
网络·网络协议·tcp/ip
YYYing.1 小时前
【C++大型项目之高性能服务器框架 (一) 】一切物语的开始:日志系统&配置系统篇
服务器·高并发·高性能·c/c++·后端框架
gis分享者1 小时前
Linux 网络层 IP 协议与网段划分实战指南
linux·运维·tcp/ip
加油码1 小时前
Linux 信号详解:从 Ctrl+C 到进程异常退出,真正理解信号机制
linux·服务器·c++
中议视控1 小时前
网络可编程中央控制系统与4K坐席分布式节点的TCP/UDP协议对接技术
网络·分布式·tcp/ip
keyipatience1 小时前
27,28,29进程通信和匿名管道详解
linux·ubuntu·centos
Shadow(⊙o⊙)1 小时前
QT常用控件3.0,font字体设置,toolTip提示,focusPolicy焦点定位原则,中型控件StyleSheet样式表。
服务器·开发语言·前端·c++·qt
勇宝趣学前端1 小时前
RustDesk 私有远程控制服务器部署
运维·服务器