传输层之TCP/UDP 核心原理全解析:从协议基础到实战机制

文章目录

    • [1. 关于传输层](#1. 关于传输层)
      • [1.1 核心定位](#1.1 核心定位)
      • [1.2 两大核心协议(TCP/IP体系)](#1.2 两大核心协议(TCP/IP体系))
      • [1.3 核心功能](#1.3 核心功能)
      • [1.4 关键特点](#1.4 关键特点)
      • [1.5 理解](#1.5 理解)
    • [2. UDP](#2. UDP)
      • [2.1 UDP协议概念](#2.1 UDP协议概念)
      • [2.2 UDP首部结构(8字节,固定长度)](#2.2 UDP首部结构(8字节,固定长度))
      • [2.3 核心特性](#2.3 核心特性)
      • [2.4 基于UDP的应用层协议](#2.4 基于UDP的应用层协议)
      • [2.5 补充理解](#2.5 补充理解)
    • [3. TCP](#3. TCP)
      • [3.1 TCP协议的概念](#3.1 TCP协议的概念)
        • [1. "控制"的核心含义:掌控数据传输的全流程](#1. “控制”的核心含义:掌控数据传输的全流程)
        • [2. 网络传输的本质:缓冲区之间的拷贝](#2. 网络传输的本质:缓冲区之间的拷贝)
        • [3. 缓冲区的底层实现](#3. 缓冲区的底层实现)
        • [4. TCP的核心定位](#4. TCP的核心定位)
      • [3.2 Tcp报头字段预览](#3.2 Tcp报头字段预览)
        • [1. 报头整体结构](#1. 报头整体结构)
        • [2. 标准报头核心字段预览](#2. 标准报头核心字段预览)
        • [3. 报头的内核实现逻辑](#3. 报头的内核实现逻辑)
      • [3.3 4位首部长度:解析报头的关键](#3.3 4位首部长度:解析报头的关键)
      • [3.4 TCP 32位序号:可靠传输与按序交付的核心标识](#3.4 TCP 32位序号:可靠传输与按序交付的核心标识)
      • [3.5 十六位窗口大小:流量控制指令的唯一载体](#3.5 十六位窗口大小:流量控制指令的唯一载体)
      • [3.6 TCP报头标志位:报文类型与通信状态的核心标识](#3.6 TCP报头标志位:报文类型与通信状态的核心标识)
      • [3.7 TCP 16位紧急指针:带外数据的定位工具](#3.7 TCP 16位紧急指针:带外数据的定位工具)
    • [4. TCP 确认应答机制:可靠传输的核心保障](#4. TCP 确认应答机制:可靠传输的核心保障)
    • [5. TCP超时重传机制:丢包恢复的核心保障](#5. TCP超时重传机制:丢包恢复的核心保障)
    • [6. TCP连接管理机制:从建立到释放的全流程控制](#6. TCP连接管理机制:从建立到释放的全流程控制)
      • [6.1 连接建立:三次握手](#6.1 连接建立:三次握手)
      • [6.2 连接释放:四次挥手](#6.2 连接释放:四次挥手)
    • [7. 滑动窗口:流量控制的核心](#7. 滑动窗口:流量控制的核心)
    • [8. 流量控制](#8. 流量控制)
    • [9. 拥塞控制](#9. 拥塞控制)
    • [10. 延迟应答](#10. 延迟应答)
    • [11. TCP通信常见异常情况及处理机制](#11. TCP通信常见异常情况及处理机制)

1. 关于传输层

传输层是OSI七层模型的第四层 、TCP/IP模型的运输层 ,核心作用是为两台主机的应用程序之间提供端到端的通信服务,是网络通信中「端到端数据传输」的核心层。

1.1 核心定位

介于网络层(负责跨网络的主机到主机通信)和应用层(具体业务应用)之间,屏蔽底层网络的差异(如丢包、延迟、路由变化),给应用层提供可靠/高效 的端到端数据传输能力,简单说:网络层管主机互通,传输层管进程互通

1.2 两大核心协议(TCP/IP体系)

  1. TCP(传输控制协议)

    • 面向连接、可靠传输、面向字节流
    • 提供三次握手建连四次挥手断连超时重传拥塞控制流量控制等机制,保证数据无差错、按序、不丢失、不重复传输
    • 适用于对可靠性要求高的场景:HTTP/HTTPS、FTP、SMTP等
  2. UDP(用户数据报协议)

    • 无连接、不可靠传输、面向数据报
    • 无建连/断连过程,头部开销小(8字节),传输速度快,仅做简单的数据包封装和发送
    • 适用于对实时性要求高、可容忍少量丢包的场景:视频直播、语音通话、DNS查询、游戏数据传输等

1.3 核心功能

  1. 端口寻址 :通过源端口目的端口区分同一主机上的不同应用进程(如80端口对应HTTP、443对应HTTPS),实现「进程级通信」。
  2. 分段与重组:将应用层的大数据块分段(加传输层头部),让底层网络能高效传输;接收端再将分段的数据包重组为完整数据。
  3. 差错控制:TCP通过校验和、确认应答(ACK)、超时重传实现;UDP仅提供校验和,无重传机制。
  4. 流量控制:TCP通过滑动窗口机制,避免发送方发送速度过快导致接收方缓冲区溢出。
  5. 拥塞控制:TCP通过慢启动、拥塞避免、快速重传/恢复等机制,适配网络链路的拥塞状态,减少网络丢包。
  6. 复用与分用
    • 复用:同一主机的多个应用进程,可通过同一个传输层协议(TCP/UDP)、同一个网络层IP地址,利用不同端口同时传输数据;
    • 分用:接收端根据数据包的目的端口,将数据准确交付给对应的应用进程。

1.4 关键特点

  • 端到端:通信双方是源主机的应用进程目的主机的应用进程,而非中间网络设备(路由器、交换机仅处理下三层)。
  • 协议可选:应用层可根据需求选择TCP(可靠)或UDP(高效),传输层仅负责实现对应协议的能力。
  • 独立于底层:屏蔽物理层、数据链路层、网络层的技术差异(如以太网、WiFi、IPV4/IPV6),给应用层提供统一的通信接口。

1.5 理解

把网络通信比作「快递寄件」:

  • 应用层:寄件人和收件人(具体的应用进程);
  • 传输层:快递公司(TCP=顺feng,可靠但稍慢;UDP=普通快递,快速但无保价),负责给包裹贴「快递单(端口+头部)」,处理「丢件重发(TCP)」「快速配送(UDP)」;
  • 网络层:物流路线规划(IP地址),负责从寄件地到收件地的路由;
  • 下三层:快递的运输工具(货车、飞机)和中转站,负责实际的物理传输。

2. UDP

2.1 UDP协议概念

UDP(用户数据报协议)是TCP/IP传输层无连接、轻量、不可靠 的核心协议,核心作用是为应用层提供高效、低延迟的端到端数据传输,仅做最基础的数据包封装与交付,不做额外可靠性保障。

2.2 UDP首部结构(8字节,固定长度)

按字节顺序分为4个字段,各占2字节:

  • 源端口:发送方应用进程的端口(可选,填0则接收方无需回复);
  • 目的端口:接收方应用进程的端口(核心,用于分用);
  • 长度:UDP数据报总长度(首部+数据,最小8字节,最大65535字节)
  • 校验和:校验首部+数据的完整性,可选(IPv4中可填0表示不校验,IPv6中必选)。

udp报头中的udp长度字段是16个比特位,也就是两个字节,可以表示2^16次方 = 65536个字节大小。含义是整个数据报的大小,那么除去报头的八个字节,数据部分最大因该是65536 - 8 = 65528 个字节,大约是64kb。

2.3 核心特性

  1. 无连接:通信前无需三次握手建立连接,发送方直接封装数据报发送,接收方直接接收,也无四次挥手断连过程,通信开销极低。
  2. 不可靠传输 :不保证数据按序到达、不丢失、不重复 ,仅提供首部校验和做简单差错检测,检测到错误直接丢弃数据包,无重传、确认应答(ACK)机制。
  3. 面向数据报:以「数据报」为传输单位,应用层传多少数据,UDP就封装多少(需符合MTU限制),接收方一次性接收完整数据报,不会拆分/重组(超出缓冲区则丢弃)。
  4. 轻量低开销 :UDP首部仅8字节(远少于TCP的20字节最小首部),字段简单,协议处理逻辑少,传输延迟低、资源占用小。
  5. 缓冲区:UDP没有真正意义上的 发送缓冲区。发送的数据会直接交给内核,由内核将数据传给网络层协议进行后续的传输动作;UDP具有接收缓冲区,但是这个接收缓冲区不能保证收到的UDP报的顺序和发送UDP报的顺序一致;如果缓冲区满了,再到达的UDP数据就会被丢弃;
  6. 支持单播/多播/广播:不仅能一对一单播,还支持一对多多播、一对所有广播,适合数据群发场景(如局域网设备发现、视频组播)。

2.4 基于UDP的应用层协议

  • NFS: 网络文件系统
  • TFTP: 简单文件传输协议
  • DHCP: 动态主机配置协议
  • BOOTP: 启动协议(用于无盘设备启动)
  • DNS: 域名解析协议当然, 也包括你自己写UDP程序时自定义的应用层协议;

2.5 补充理解

  1. UDP像 「寄平信」 :写好地址直接投邮筒,无需提前确认对方是否接收,邮局仅负责投递,不保证一定送到、按时送到,也不保证信件顺序;优点是快、省事、成本低,适合寄不重要的短消息。
    (补充:UDP本身不可靠,但可在应用层实现自定义可靠性,比如添加确认、重传、按序重组机制,典型如RTSP、RTP协议,既保留UDP的低延迟,又满足业务级可靠性需求。)
  2. Q:端口号port为啥是16位?
    A:因为内核协议是16位。
  3. UDP数据报的报文之间是有边界的。定长报头中写明了报文长度。所以UDP数据报传输到操作系统中,操作系统可以将UDP数据报之家分开,因此,UDP协议是面向数据报的。

3. TCP

3.1 TCP协议的概念

TCP(传输控制协议)是互联网最核心的传输层协议,核心价值是在不可靠的IP网络上,为应用层提供可靠、可控的端到端字节流传输服务,其"控制"二字是理解TCP的核心,具体可梳理为以下核心要点:

1. "控制"的核心含义:掌控数据传输的全流程

应用层调用write/recv/send/read等接口时,并非直接把数据发送到网络,而是将数据拷贝到操作系统内核的TCP发送缓冲区

TCP协议自主"控制"以下关键环节,无需应用层干预:

  • 何时发送缓冲区中的数据(如等待缓冲区积累到一定大小、或满足超时条件);何时发
  • 一次发送多少数据(结合接收方窗口、网络拥塞状态动态调整);发多少
  • 传输出错如何处理(丢包重传、超时重连、乱序重组等)。发错怎么办

因此,这些网络IO函数本质是"数据拷贝函数",而非"直接发送/接收函数"。

2. 网络传输的本质:缓冲区之间的拷贝
  • 数据发送:应用层数据 → 本机TCP发送缓冲区 → 网卡 → 网络 → 对方网卡 → 对方TCP接收缓冲区 → 对方应用层;
  • 类比文件操作:和write写文件时"数据先到文件缓冲区、再刷到磁盘"逻辑一致,只是把"磁盘设备"换成"网卡设备",底层由操作系统统一管理;
  • 全双工通信:通信双方各自拥有独立的发送缓冲区和接收缓冲区,可同时向对方拷贝数据(你给我拷贝的同时我也能给你拷贝),收发互不干扰,因此套接字/文件描述符支持可读可写。
3. 缓冲区的底层实现
  • 缓冲区是操作系统划分的内存空间,逻辑上由大量4KB内存块+struct_page(内存块管理结构)组成,遵循"先描述、再组织"的内核管理逻辑;
  • 网络套接字对应文件描述符,内核通过struct_file对象管理:文件操作时该对象指向磁盘,网络操作时指向网卡,实现"上层接口不变,下层设备可切换"。
4. TCP的核心定位

TCP专为"长距离网络传输易出错"设计,通过对"数据发送时机、发送量、异常处理"的全流程控制,保证数据无丢失、无重复、按序到达,是HTTP/HTTPS等应用层协议的底层支撑。


3.2 Tcp报头字段预览

TCP报头整体分为三部分:标准报头(固定20字节)可选字段(可忽略)有效载荷(待传输的数据)

其字段远多于UDP的核心原因是TCP需通过这些字段实现可靠性保障。

1. 报头整体结构
组成部分 特点 核心作用
标准报头 固定20字节,必选 承载TCP核心控制逻辑
可选字段 0~40字节,可选 优化TCP性能(如MSS、时间戳)
有效载荷 无固定长度 实际要传输的应用层数据
2. 标准报头核心字段预览
  • 源端口/目的端口(各16位):和UDP完全一致,用于进程寻址,将数据交付给上层指定应用协议(如80端口对应HTTP);
  • 其余字段(如序列号、确认号、窗口大小、标志位等):TCP特有的控制字段,是实现"可靠性"的核心,包括:
    ✅ 序列号/确认号:保证数据按序、不丢失、不重复;
    ✅ 窗口大小:实现流量控制;
    ✅ 标志位(SYN/ACK/FIN等):实现连接建立/释放、数据推送等;
    ✅ 校验和:检测报头+数据的完整性。
3. 报头的内核实现逻辑

TCP报头在操作系统内核中以位段类型定义(类似结构体):

  • 封装TCP报头时,内核会定义一个该位段类型的变量;
  • 按字段含义填充源端口、目的端口、序列号等属性;
  • 这个过程和填充sockaddr结构体(绑定IP/端口)的逻辑完全一致,都是"定义结构体变量 → 填充字段 → 供协议使用"。

TCP报头源码如下:


3.3 4位首部长度:解析报头的关键

Q:报头如何和有效载荷分离?

Q:有效载荷如何交付给上层?

A :通过4位首部长度。

  1. 4位首部长度字段介绍

    TCP标准报头固定20字节,而"4位首部长度"字段是解析报头的关键,其设计逻辑和作用可梳理如下:

    • 字段本身:4位二进制(取值0000 ~ 1111),对应十进制0~15,单看数值远小于20,无法直接表示报头长度;
    • 关键规则 :该字段的基本单位是4字节 (而非1字节),因此实际表示范围为 0×4 ~ 15×4,即0~60字节;
    • 标准场景 :无选项时,该字段值为5 (二进制0101),对应 5×4=20字节,恰好匹配标准报头长度。
      (也能理解为啥要由一个保留六位的字段了,就是为了凑20)
  2. 字段的核心作用

    • 区分报头总长度:报头=标准报头(20字节)+ 可选字段(0~40字节),通过该字段可计算出报头总长度(如值为6则对应24字节,即20字节标准报头+4字节选项);
    • 精准分离报头与有效载荷:读取缓冲区数据时,先通过该字段计算出报头总长度,截取对应字节数得到完整报头,剩余部分即为有效载荷。
  3. 报头解析流程

    1. 从缓冲区截取前20字节(标准报头),读取"4位首部长度"字段;
    2. 按"字段值×4字节"计算出报头总长度(无选项时为5×4=20字节);
    3. 按计算出的总长度从缓冲区分离出完整报头,剩余部分为有效载荷;
    4. 从报头中解析出16位源/目的端口号(和UDP逻辑一致),将有效载荷交付给上层应用。

3.4 TCP 32位序号:可靠传输与按序交付的核心标识


Q:为啥说TCP协议是可靠传输?
A: 因为TCP协议存在序号和确认序号,是TCP协议中应答机制 的载体。

关于应答机制简介: 发送端向接收端发送报文后,接收端要对此做出响应,做法是向发送端做出应答 。若发送端接收到接收端传来的应答则认为历史数据已经被可靠传输,若一定时间没有收到应答,则认为数据丢失,会进行超时重传
意义是: 发送端可以保证历史消息的可靠性。但是通信中,最新的报文永远没有应答 ,最新报文的可靠性无法保证。
应答机制的原则是: 不对应答做应答

以上是TCP实现可靠性的大致设计逻辑,详细介绍如下:

TCP报头中的32位序号是实现数据按序到达 、支撑确认应答机制的核心字段,也是解决捎带应答下乱序、身份冲突问题的关键设计。
其本质是对传输字节流的唯一编号,从根本上为TCP可靠传输奠定基础,具体核心设计和作用如下:

  1. 序号的核心诞生背景:解决可靠传输的本质问题

    网络协议不存在100%的可靠性,最新发送的消息始终无法获得应答兜底,但TCP可通过序号+确认序号保证最新消息之前的所有数据可靠

    同时单方向的应答交互效率极低,为了解决效率低的问题 ,TCP采用同时发送一批报文 ,并且采用捎带应答机制 (应答报文同时携带待发送数据)提升传输效率,但这样会引发乱序问题 ,而序号正是解决该问题的核心,让乱序到达的报文能按序重组 ,从源头避免乱序导致的不可靠。

  2. 序号的本质定义:发送缓冲区字节流的天然编号

    应用层数据拷贝到TCP发送缓冲区后,会以字节为单位连续存储(如char outbuffer[N])。
    32位序号就是该字节流的天然数组下标 ,表征发送数据块最后一个字节的编号,是对传输数据的唯一标识,让每一个字节都有可追溯的"身份编号"。

  3. 序号的核心作用:TCP可靠传输的基础支撑

    (1)保证数据按序到达,解决乱序问题

    捎带应答会让多个报文可能乱序到达接收方,而接收方可根据报头中的32位序号,将乱序报文按序号重新排序,恢复原始字节流,这是序号最核心的作用,直接解决了捎带应答下的乱序不可靠问题。

    (2)作为确认序号的填充依据,支撑确认应答机制

    接收方返回的32位确认序号 ,是以收到的报文序号为基础计算(确认序号=收到的序号+1 ),序号是确认序号生成的唯一依据,二者共同构成TCP确认应答机制的核心,实现数据接收状态的精准反馈。

    (3)判断数据是否丢包,辅助可靠传输

    通过序号的连续性可直接判断数据是否丢包:若接收方发现序号不连续,即可判定中间存在丢包,为后续重传机制提供判断依据。

  4. Q:发送端发送序号1000,接收端可以把1000变成1001然后返回给接收端,为什么还要搞一个32位确认序号呢?
    A: 接收端返回的应答可能也会携带数据 (捎带应答)。

    假设客户端发1000,然后服务器的捎带应答中包含了2000数据,所以服务器需要同时向客户端发1001和2000的序号。

    因为tcp双方地位是等同的,所以协议必须把两个序号分开,不能复用。

    应答可能是捎带应答,有双重身份,既有应答也有数据,所以需要分开,服务器在给客户端发消息,同时客户端可能也在对服务器发消息 。

  5. 序号与批量传输、应答丢失的适配性

    序号支持TCP的批量数据传输 :客户端可连续发送一批带有序号的报文,只要收到这批报文的最终应答,即可确认所有数据被接收方成功接收;

    同时序号的连续性也让TCP允许少量应答丢失:若客户端发送1000、2000、3000序号的报文,仅收到3001的确认序号,即可通过序号的连续性判定1000、2000序号的数据已被接收,无需单独的应答,提升传输效率。

  6. 序号的核心价值总结

    32位序号是TCP字节流传输的唯一身份标识 ,核心作用是保证数据按序到达 ,同时是接收方填充确认序号的直接依据,为确认应答机制提供数据化支撑;

    其设计适配了TCP捎带应答和全双工通信的特性,解决了高效传输下的乱序、报文身份冲突问题,还能辅助判断数据丢包,是TCP实现可靠传输的核心基础字段,与确认序号共同构成TCP可靠传输的核心骨架。


3.5 十六位窗口大小:流量控制指令的唯一载体

当发送端一直发送数据,但是接收方的接收能力有限,就会导致接收方发送出来的大量数据被丢失。

为了解决该问题,TCP报头中包含16位窗口大小字段

TCP报头中的16位窗口大小字段是实现流量控制的核心,其设计目标是从源头避免接收方缓冲区溢出导致的丢包(也可以向发送端传递加快传输数据的信号),具体功能和工作逻辑如下:

  1. 字段的核心作用

    该字段是接收方向发送方传递「流量控制指令」的唯一载体,其值直接表示接收方当前TCP接收缓冲区的剩余可用字节数发送方需以此为依据调整发送速度,防止接收缓冲区被写满引发丢包。

  2. 字段的工作机制

    • 载体场景:窗口大小填充在接收方的TCP确认应答报文中(该报文是完整TCP报文,可无数据但必须包含完整报头);
    • 双向控制:通信双方在互发的确认应答报文中,均会填入自身接收缓冲区的剩余大小,因此能实现双向流量控制(客户端/服务器均可告知对方自己的缓冲区状态);
    • 速度调整逻辑:发送方收到窗口大小后,若数值小(缓冲区快满)则减速/暂停发送,若数值大则正常发送,从源头避免接收方来不及处理数据。
  3. 字段未初始化的场景处理

    首次发送数据时,发送方暂无接收方的窗口大小信息,此时依赖TCP重传机制兜底:若发送方超时未收到确认应答,默认数据未送达,触发重传(无需提前获知窗口大小)。

  4. 字段的价值

    窗口大小字段让流量控制成为可能,相比单纯依赖丢包重传更高效------重传虽能恢复数据,但已浪费网络资源;而通过该字段提前调整发送速度,可从根源避免丢包,减少资源消耗(但无法替代重传机制,二者互补保障TCP可靠性)。


3.6 TCP报头标志位:报文类型与通信状态的核心标识

Q:为什么要存在标志位?
A: 区分Tcp报文的类型

一个服务器可能会收到来自多个客户端的的请求,Tcp通信时要建立连接,最后Tcp还要断开连接,在这中间它还要进行正常的数据通信。

所以Tcp报文有的是建立连接的,有的是进行数据发送的,有些是进行断开连接的。

所以服务器收到的Tcp报文本身是有类型的 ,不同的报文类型决定了服务器要做不同的动作。

所以接收方如何得知报头的类型是什么呢? 就是通过Tcp里面存在6个标志位。

TCP报头中的6个标志位(SYN/ACK/FIN/PSH/RST/URG)是区分TCP报文类型、控制通信状态的核心字段

其作用是让接收方明确报文的功能,从而执行对应的处理逻辑。

各个信号介绍如下:

  1. SYN:连接建立请求标志

    • 功能:当该位为1时,表明此报文是请求建立TCP连接的报文,仅在三次握手阶段使用;
    • 场景:三次握手的第1次(客户端→服务器)、第2次(服务器→客户端)报文会设置SYN,正常数据通信阶段SYN始终为0。
  2. ACK:确认应答标志

    • 功能:当该位为1时,表明此报文是确认应答报文,用于反馈已收到对方数据;
    • 场景:除三次握手的第1次报文外,其余所有TCP报文都会设置ACK(包括捎带应答),是TCP可靠传输的基础标识之一。
  1. FIN:连接断开请求标志
    • 功能:当该位为1时,表明此报文是请求断开TCP连接的报文,仅在四次挥手阶段使用;
    • 场景:四次挥手的第1次、第2次报文会设置FIN,正常数据通信阶段FIN始终为0。
  1. PSH:数据推送提示标志

    • 功能:当该位为1时,提醒接收方应用层立即从TCP接收缓冲区读取数据,避免缓冲区堆积导致流量控制僵持;
    • 场景:若接收方长时间不读取缓冲区数据,发送方会在报文中设置PSH,强制触发接收方的读取动作;若接收方仍不读取,发送方可能关闭连接。
  2. RST:连接重置标志

    • 功能:当该位为1时,表明当前连接异常,需重新建立连接
    • 场景:常见于连接状态不一致的情况(如三次握手第3次ACK丢失,客户端认为连接已建立、服务器未收到ACK),服务器会发送带RST的报文,通知客户端重置连接,重新发起三次握手;也适用于服务器压力过大、连接被强制中断等异常场景。
  1. URG:紧急数据标志
    • 功能:当该位为1时,表明报文包含需优先处理的"紧急数据",配合"16位紧急指针"(标识紧急数据在有效载荷中的偏移量)使用;
    • 限制:TCP协议中紧急数据默认仅支持1字节(带外数据);
    • 场景:用于优先级最高的通信场景(如客户端检测到服务器无响应时,发送紧急数据查询服务器状态),接收方会优先处理该数据,绕过正常的按序处理逻辑。

3.7 TCP 16位紧急指针:带外数据的定位工具

TCP报头中的16位紧急指针 是配合URG标志位使用的字段,核心作用是定位报文中需优先处理的"紧急数据"(带外数据),是实现TCP数据"插队"处理的关键,具体逻辑如下:

这里就记一个向网盘传数据的例子,在向网盘传数据的时候有停止和暂停选项,当用户点击停止的时候,也是向服务器发送一个数据报,但是这个包就不能按部就班的等待处理,而是要优先被服务器处理。这里用到的技术就是紧急指针。

  1. 紧急指针的核心关联:与URG标志位绑定

    紧急指针本身不独立生效,需依赖URG标志位的状态:

    • URG=0时:紧急指针无效,报文中无需要优先处理的数据;
    • URG=1时:紧急指针被激活,其值表示紧急数据在有效载荷中的偏移量,接收方会根据该偏移量定位到紧急数据,优先处理。
  2. 紧急指针的功能限制:仅支持1字节紧急数据

    TCP协议中,紧急指针仅能标识一个位置的偏移量 (无法表示数据长度范围),紧急指针表示这个报文中要优先处理的数据在有效载荷中的偏移量。因此默认仅支持1字节的紧急数据------接收方定位到偏移量对应的字节后,仅处理该字节作为紧急数据。

  3. 紧急指针的实际应用场景:服务状态查询

    紧急指针的典型使用场景是客户端查询无响应服务器的状态

    • 当客户端连接的服务器未断连(无四次挥手)但无响应时,客户端可发送带URG=1的报文,并通过紧急指针定位1字节的"状态查询指令"(问问服务器咋啦);
    • 服务器收到后,会绕过正常的按序处理逻辑,优先读取该紧急数据,将自身当前状态(如计算中、IO阻塞等)以1字节编码的形式,通过紧急指针对应的位置返回给客户端;
    • 这类优先传输的紧急数据在应用层被称为"带外数据",是TCP仅有的"插队"传输机制。

recv函数 的第四个参数flags有一个叫做MSG_OOB的选项可供设置,OOB表示带外数据(out-of-band)的简称,就是紧急数据,就可以在使用recv 函数进行读取,并设置MSG_OOB选项:

  1. 紧急指针的核心价值
    紧急指针是TCP在"面向字节流、按序传输"的基础上,为高优先级数据 提供的特殊定位工具,通过与URG标志位配合,实现了数据的"优先处理",解决了常规按序传输无法满足的紧急通信需求。

4. TCP 确认应答机制:可靠传输的核心保障

确认应答机制是TCP实现"可靠传输"的基础逻辑,核心是发送方通过接收方的"确认报文",判断自己发送的数据是否被成功接收,具体可简化为3个关键要点:

  1. 核心载体:序号+确认序号

    • 序号:发送方给每个字节分配唯一编号(如发送1000字节数据,序号为该数据块的最后一个字节编号);
    • 确认序号:接收方收到数据后,在应答报文中填入"已接收字节的最大序号+1"(如收到序号1000的报文,确认序号填1001),表示"我已收到1000及之前的所有数据,下次请从1001开始发"。
  2. 工作流程

    1. 客户端发送带序号的报文(如序号1000);
    2. 服务器收到后,返回带确认序号1001的应答报文;
    3. 客户端收到确认序号,即可确认"序号1000的数据已被成功接收"。
  3. 关键特点

    • 捎带应答:确认报文可同时携带待发送的数据(不单独发应答,提升效率);
    • 允许批量确认:若收到序号1000、2000、3000的报文,可直接返回确认序号3001,无需逐个应答;
    • 超时重传兜底:若发送方超时未收到确认,会重发对应数据(解决丢包问题)。

核心作用

通过"序号标识数据+确认序号反馈接收状态",实现了数据的无丢失、无重复、按序到达,是TCP可靠传输的核心逻辑。


5. TCP超时重传机制:丢包恢复的核心保障

超时重传机制是TCP可靠传输的关键兜底机制,核心是发送方为已发送数据设置超时计时器,若超时未收到对应确认应答(ACK),则自动重传该数据,解决网络丢包导致的数据传输失败问题,具体逻辑如下:

  1. 核心工作原理

    • 发送方发送带序号的TCP报文后,立即启动一个超时计时器(超时时间RTO会根据网络延迟动态调整);
    • 若在计时器超时前收到接收方的确认应答(含对应确认序号),则撤销计时器,确认数据已成功交付;
    • 若计时器超时仍未收到确认应答,默认数据在网络中丢失(或确认应答丢失),发送方会重新发送该报文 ,并重设计时器。(深层意义:丢包是指没有收到ACK并且超过特定的时间间隔)
  2. 关于丢包的两种情况:

    1. 发送方发出去的数据直接丢了,接收方没收到数据。
    2. 接收方收到数据了,但是发给发送方的应答报文丢了。

但是无论是什么情况,统一规定为丢包,都会进行补发;

但是如果补发次数多了,会判定链接出问题了,会申请重新建立链接。

所以服务器可能会收到重复报文,所以服务器需要"去重",所以报文的序号还有一个重要的作用就是"去重"

当发送缓冲区的数据发出去后,操作系统不会立即将数据从缓冲区中删除或覆盖,会暂时保存,在需要时进行超时重传。

  1. 关键细节

    • 超时时间(RTO)自适应:RTO并非固定值,会根据网络往返时间(RTT)动态调整(如网络延迟高则调大RTO,避免误重传);

    • Linux中(BSD Unix和Windows也是如此), 超时以500ms为一个单位进行控制 , 每次判定超时重发的超时 时间都是500ms的整数倍,如果重发一次之后, 仍然得不到应答, 等待 2 * 500ms 后再进行重传。

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

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

    • 重传后处理:重传报文的序号与原报文一致,接收方通过序号去重,避免重复处理;

    • 与确认应答机制配合:超时重传是确认应答机制的补充------确认应答解决"正常接收反馈",超时重传解决"无反馈时的丢包恢复"。

  2. 适用场景

    • 数据报文在网络中丢失(如路由器转发失败、链路中断);
    • 接收方的确认应答报文丢失(发送方误以为数据未送达);
    • 网络延迟过高,确认应答超出超时时间到达。

6. TCP连接管理机制:从建立到释放的全流程控制

TCP连接管理机制是保障双向可靠通信的基础 ,核心通过「三次握手建立连接 」「四次挥手释放连接 」两个核心流程,配合状态机管理,实现连接的有序创建与释放,具体逻辑如下:

6.1 连接建立:三次握手

双方在进行网络通信前需要先建立连接,这个建立连接的过程我们称之为三次握手

connect 发起三次握手,但是这内部握手的过程则是由客户端操作系统自动完成的,accept不参与三次握手,由服务端操作系统自动完成。

最开始客户端和服务器都处于CLOSED状态,但是服务器为了能够接收客户端发来的连接请求,所以服务器变为了LISTEN监听状态。

  1. 第一次握手(客户端→服务器) :客户端发送 SYN=1 报文,携带自身初始序列号ISN_A,进入 SYN_SENT 状态 ;(SYN_SENT的意义是:已发送建立请求的连接)
  2. 第二次握手(服务器→客户端) :处于监听状态的服务器响应 SYN=1、ACK=1 报文,将连接放进内核等待队列中,并向客户端发起第二次握手,发出SYN+ACK后(捎带应答),进入 SYN_RCVD 状态 ;(SYN_RCVD 意义是:已收到建立请求的连接)
  3. 第三次握手(客户端→服务器) :客户端发送 ACK=1 报文,确认序号设为ISN_B+1,双方均进入 ESTABLISHED 状态,连接建立完成。(ESTABLISHED意义是:已经建立连接)

状态存储在套接字对象中

上面的SYN和ACK,就是Tcp报头字段中的那两个标志位,而对于第二次握手,除了应答,服务器也要主动建立连接,所以服务器也要发个SYN给客户端,因为Tcp协议中,双方主机地位是相等的 :"你要和我通信,那么我也要和你通信",总结:Tcp是全双工的
Q:为什么要进行三次握手?
A:

  • 理由1:三次握手可以用最短的方式进行全双工验证 。以A端向B端发起连接请求为例:
    第一次握手:能确定A是否能发;
    第二次握手:能确定B是否能收和是否能发;
    第三次握手:能确定A是否能收。
    本质:验证网络是否流畅,能否支持全双工。
  • 理由2:以最小成本确定双方通信意愿(通信双方都想进行可靠传输)。
    Q:三次握手为什么会成为三次握手,而不是四次握手?
    A: 因为在第二次握手时做了捎带应答 ,所以成了三次握手。客户端发连接,服务器都要无脑接收,所以可以把ACK捎带。

但断开连接(四次挥手)不一定,可能服务器正在向客户端传数据,不能立马断开,只能单独做一个ACK应答。
Q:套接字的接口和三次握手的关系是什么?

Socket API(listen/connect/accept)是用户层触发、控制三次握手的"入口",而三次握手的实际报文交互、状态切换等核心工作,全部由操作系统内核完成------API只负责"发指令"和"等结果",不参与底层网络交互。

  1. 服务器端:listen()------准备接收连接,进入LISTEN状态

    • 用户层操作 :服务器调用listen(sockfd, backlog),传入监听的socket描述符和等待队列长度;
    • 内核行为
      ① 将服务器的套接字状态从CLOSED改为LISTEN(监听状态);
      ② 创建一个"半连接队列 "(存储正在三次握手的连接)和"全连接队列 "(存储三次握手完成的连接);
      ③ 内核开始监听指定端口,等待客户端的SYN报文。
    • 关键:listen只做"准备工作",不触发任何三次握手报文,只是让服务器具备接收连接的能力。
  2. 客户端:connect()------发起三次握手,等待结果返回

    • 用户层操作 :客户端调用connect(sockfd, &serv_addr, len),传入服务器IP和端口;
    • 内核行为(核心)
      ① 客户端内核自动发送第一次握手 :封装SYN报文,发送给服务器,客户端套接字进入SYN_SENT状态;
      ② 等待服务器返回第二次握手(SYN+ACK),收到后客户端内核自动发送第三次握手 (ACK),客户端套接字进入ESTABLISHED状态;
      ③ 若上述过程成功,connect函数返回0(成功);若超时/服务器拒绝,返回错误(失败)。
    • 关键
      • connect不"处理"三次握手,只"触发"并"等待结果"------底层的SYN发送、ACK接收、状态切换全是内核做的;
      • connect返回=三次握手结果已定(成功/失败),用户层无需关心底层报文交互。

connect(sockfd, &serv_addr, len)
sockfd :就是你之前调用socket()函数创建的「套接字文件描述符」(简称 fd)------ 相当于给操作系统指定 "要用哪个套接字去连接目标服务器"。
&serv_addr :指向「要连接的目标端(服务器)网络地址」的指针------ 简单说就是告诉操作系统"我要连接到哪个 IP、哪个端口的服务器"。
len:告诉操作系统第二个参数addr所指向的地址结构体的「实际字节长度」

  1. 服务器端:accept()------拉取已完成的连接到用户层

    • 内核的"隐藏工作"
      服务器收到客户端第三次握手的ACK后,内核会:
      ① 将该连接从"半连接队列"移到"全连接队列";
      ② 为这个新连接创建一个新的套接字结构体 (包含双方IP、端口、序号、窗口等连接信息),状态设为ESTABLISHED
      ③ 这个新连接此时只存在于内核的全连接队列中,用户层无法操作。
    • 用户层操作 :服务器调用accept(listen_fd, &cli_addr, &len)
    • 内核行为
      ① 从全连接队列中取出一个已完成三次握手的连接;
      ② 为该连接创建一个新的文件描述符(conn_fd),返回给用户层;
      ③ 用户层拿到conn_fd后,才能通过read/recv、write/send与客户端交互。
    • 关键
      • accept不参与三次握手,只负责"提取成果"------它提取的是内核已经完成三次握手的连接;
      • 若全连接队列为空,accept会阻塞,直到有新的连接完成三次握手。
  2. 数据交互:read/recv/write/send------与三次握手无关,仅用于连接建立后

    当accept返回conn_fd(客户端connect返回成功),双方套接字都处于ESTABLISHED状态,此时调用read/recv(读数据)、write/send(写数据),只是基于已建立的TCP连接收发数据,和三次握手无直接关系。

Q:什么是全连接队列?listen函数的第二个参数有什么用?
A: 在我们申请连接时,服务器就已经存在大量已经建立好的连接,但是如果服务器的上层没有accept,把连接拿到上层去,此时这些处在内核中的连接叫做"全连接 ",所以操作系统就必须把这些"全连接 "管理维护起来。

每一个连接都是是数据结构,所以底层采用队列 的形式来管理,这个队列也类似于生产者消费者模型,所以listen的第二个参数表示这个"全连接"队列的最大长度 ,长度为 backlog + 1

队列里的链接都是三次握手全部完成的链接,然后等待上层accept调用。

但是当队列满了,即使三次握手成功了,服务器也无法把新连接放到队列里了。
Q:什么是半连接队列

A: 半连接队列(也叫SYN队列)是服务器内核中专门存储"未完成三次握手、处于SYN_RCVD状态连接"的缓冲区

是TCP应对连接建立压力、防范SYN洪水攻击的核心机制,具体逻辑如下:

  1. 半连接队列的核心定义

    1. 连接状态特征 :队列中的连接仅完成三次握手的"前两次交互"------服务器收到客户端的SYN(第一次握手),回复了SYN+ACK(第二次握手),但尚未收到客户端的ACK(第三次握手),服务器套接字处于SYN_RCVD状态,这种"只完成一半握手"的连接就是「半连接」
    2. 存储目的:内核需要临时管理这些未完成的连接,等待客户端发送第三次握手的ACK,因此用"半连接队列"统一存储、维护;
    3. 生命周期:半连接不会被长期维护------内核会为每个半连接设置超时时间,若超时未收到客户端的ACK,会直接释放该连接(避免资源长期占用)。
  2. 半连接队列的核心行为

    半连接队列的核心价值体现在"全连接队列满"的场景:

    1. 正常流程:服务器收到客户端第三次握手的ACK后,连接从SYN_RCVD变为ESTABLISHED,并从半连接队列转入全连接队列,等待accept()提取;
    2. 全连接队列满时:
      • 服务器为避免资源耗尽,会故意丢弃客户端的第三次握手ACK,不将连接状态改为ESTABLISHED,让连接始终停留在SYN_RCVD状态、保存在半连接队列中;
      • 客户端因未收到服务器对ACK的"应答"(实际无此应答),会重试发送ACK,但服务器会持续丢弃,直到半连接超时被内核释放。
  3. 半连接队列与SYN洪水攻击的关联

    1. SYN洪水的本质:攻击者发送大量伪造的SYN报文,服务器回复SYN+ACK后,攻击者不发送第三次握手的ACK------这些伪造连接会长期占满半连接队列;
    2. 危害:合法客户端的SYN连接因半连接队列满无法被存储,导致正常用户无法建立连接(表现为"服务器繁忙,请稍后再试");
    3. 内核管控 :半连接队列的长度由内核参数(如tcp_max_syn_backlog)控制,且内核会定时清理超时的半连接,目的是防止队列被非法连接占满,保障服务器稳定性。
  4. 半连接队列与全连接队列的递进关系

    复制代码
    客户端SYN → 服务器接收并放入【半连接队列】(状态SYN_RCVD)→ 收到客户端ACK → 转入【全连接队列】(状态ESTABLISHED)→ 等待accept()提取
    • 半连接队列是"连接建立的临时缓冲区",全连接队列是"连接建立完成的待处理缓冲区";
    • 两个队列任一满,都会导致新连接无法正常建立,是服务器连接管理的核心限流节点。

6.2 连接释放:四次挥手

  1. 第一次挥手(主动关闭方→被动关闭方) :主动方发送 FIN=1 报文,表明无数据要发送,进入 FIN_WAIT_1 状态;
  2. 第二次挥手(被动关闭方→主动关闭方) :被动方响应 ACK=1 报文,确认序号=对方FIN报文序号+1,进入 CLOSE_WAIT 状态(此时仍可接收主动方数据);
  3. 第三次挥手(被动关闭方→主动关闭方) :被动方无数据要发送后,发送 FIN=1、ACK=1 报文,进入 LAST_ACK 状态;
  4. 第四次挥手(主动关闭方→被动关闭方) :主动方响应 ACK=1 报文,确认序号=对方FIN报文序号+1,进入 TIME_WAIT 状态(等待2MSL确保对方收到ACK),之后双方进入 CLOSED 状态。
四次挥手阶段 主动关闭方行为 fd状态 通道状态
第一次挥手 调用close()/shutdown(SHUT_WR),发送FIN fd未关闭(仍可读) 主动关闭方 → 被动关闭方:写通道关闭(不再发数据)
第二次挥手 收到被动关闭方的ACK(第二次挥手) fd仍未关闭(仅写通道关,读通道通) 被动关闭方 → 主动关闭方:读通道仍正常
第三次挥手 等待被动关闭方发送FIN fd仍未关闭 读通道仍可接收被动关闭方的数据
第四次挥手 发送ACK,等待2MSL后 内核最终关闭fd 双向通道全关

Q:为什么TCP挥手需四次而非三次?
A: 核心原因是TCP全双工通信的"双向关闭"特性------断开连接需协商双方剩余数据,无法像三次握手那样通过"捎带"合并步骤,具体总结如下:

  1. 全双工通信的双向关闭需求 :TCP连接是双向通道(读+写分离),关闭连接需分别关闭两个方向的通道。

    主动关闭方先关闭"写通道"(发FIN),被动关闭方需先确认该关闭(发ACK),此时可能仍有未发完的数据,需等数据发完后再关闭自己的"写通道"(发FIN),最后主动关闭方确认(发ACK),这四步无法省略。

  2. 三次握手的合并条件不适用 :三次握手的第二次能合并SYN+ACK,是因为服务器无需协商------收到连接请求后可立即同意并同步参数;

    但挥手时被动关闭方可能有未完成数据,无法在确认对方关闭(发ACK)的同时,同步关闭自己的通道(发FIN),只有当被动关闭方无数据可发时,才可能偶然合并ACK+FIN(形成"三次挥手"特例),但这不是普遍情况。

  3. 可靠性协商的核心需求:挥手的本质是"双向协商断开",而非握手的"单向请求-同意"。主动关闭方仅表示自己无数据可发,不代表被动关闭方也无数据,四次交互能确保双方都有足够时间处理剩余数据,避免数据丢失,这是TCP可靠传输的必然要求。

综上,四次挥手是TCP全双工特性下"优雅关闭连接"的最优设计,三次挥手无法满足双向关闭的协商需求,仅在被动关闭方无剩余数据时存在特例,不能作为通用规则。

TCP四次挥手完整流程

双方在ESTABLISHED(已建立连接)状态下正常通信,客户端主动发起断开请求,完整流程如下:

  1. 第一次挥手:客户端发起断开,进入FIN_WAIT_1状态

    • 操作方:客户端(主动关闭方)
    • 行为:客户端无数据需发送,向服务器发送「FIN标志位置1」的TCP报文,告知"我已无数据要发,请求关闭我的写通道";
    • 状态切换 :客户端从ESTABLISHEDFIN_WAIT_1(等待服务器的ACK应答)。
  2. 第二次挥手:服务器确认断开,进入CLOSE_WAIT状态

    • 操作方:服务器(被动关闭方)
    • 行为:服务器收到客户端的FIN报文,立即回复「ACK标志位置1」的应答报文,确认"已收到你的断开请求";
    • 状态切换 :服务器从ESTABLISHEDCLOSE_WAIT(此时服务器读通道已关,但写通道仍开放,可继续向客户端发送剩余数据)。
    • 补充:客户端收到该ACK后,状态从FIN_WAIT_1FIN_WAIT_2(等待服务器的FIN报文)。
  3. 第三次挥手:服务器发完数据,主动断开,进入LAST_ACK状态

    • 操作方:服务器(被动关闭方转为主动)
    • 行为:服务器发送完所有剩余数据后,向客户端发送「FIN标志位置1」的TCP报文,告知"我也无数据要发,请求关闭我的写通道";
    • 状态切换 :服务器从CLOSE_WAITLAST_ACK(等待客户端的最终ACK应答,确认数据传输完成)。
  4. 第四次挥手:客户端最终确认,进入TIME_WAIT状态

    • 操作方:客户端
    • 行为:客户端收到服务器的FIN报文,立即回复「ACK标志位置1」的最终应答报文,确认"已收到你的断开请求";
    • 状态切换
      • 客户端:从FIN_WAIT_2TIME_WAIT(需等待2MSL后,确保网络中残留的报文被处理,避免新连接受旧报文干扰);
      • 服务器:收到客户端的最终ACK后,从LAST_ACKCLOSED(彻底关闭连接,删除连接结构体、释放资源)。
  5. 客户端最终关闭:TIME_WAIT等待2MSL后

    • 客户端在TIME_WAIT状态等待2个MSL(报文最大生存时间),确保服务器已收到最终ACK、网络中无残留的TCP报文;
    • 等待结束后,客户端从TIME_WAITCLOSED,彻底关闭连接并释放资源。

Q:主动发起断开连接的一方会进入TIME_WAIT状态,为何要等待一段时间才转为CLOSED?
A: 主动关闭方(通常是客户端)进入TIME_WAIT状态后等待2MSL(两倍报文最大生存时间),核心是为了保证TCP连接关闭的"可靠性"和"安全性",具体原因拆解如下:

  1. 保障四次挥手的最终可靠性(容错性)

    • 场景:客户端发送第四次挥手的ACK报文后,若直接关闭(CLOSED),该ACK可能因网络问题丢失;
    • 后果:服务器仍处于LAST_ACK状态,收不到ACK会超时重传FIN报文,但此时客户端已关闭,无法应答,服务器会持续重传直到超时,造成服务器资源浪费(无意义的重传、连接结构体占用);
    • 解决方案:TIME_WAIT等待2MSL期间,客户端仍"存活":
      ✅ 若收到服务器重传的FIN,可重新发送ACK,确保服务器能正常收到并关闭;
      ✅2MSL时长足以覆盖服务器FIN的最大重传周期,避免服务器因ACK丢失陷入无限重传。
  2. 让网络中残留的历史报文完全消散(避免干扰新连接)

    • MSL(Maximum Segment Lifetime):TCP报文在网络中能存活的最大时长 (内核默认约30秒~1分钟,可通过cat /proc/sys/net/ipv4/tcp_fin_timeout查看);
    • 风险:若客户端直接关闭,网络中可能残留本次连接的旧报文 (如延迟的数据包、重传报文);
      若此时客户端用相同的「IP+端口」与服务器建立新连接,旧报文可能被新连接接收,导致数据错乱;(这也是为什么我们关闭服务器再立即用相同的端口启动会端口绑定失败的原因。因为这个端口还在占用。)
    • 解决方案:等待2MSL的逻辑:
      ✅ 第一个MSL:确保本端发送的最后一个ACK报文能到达服务器(或确认丢失);
      ✅ 第二个MSL:确保网络中所有残留的旧报文完全过期、被路由器丢弃;
      ✅2MSL后再关闭,新连接不会收到本次连接的历史报文,保证通信纯净。

总结:TIME_WAIT等待2MSL的核心目的

  1. 容错性保障:避免第四次挥手的ACK丢失导致服务器无意义重传FIN,降低服务器资源消耗;
  2. 安全性保障:等待网络中残留的旧报文完全消散,防止旧报文干扰后续同端口的新连接;
  3. 补充:2MSL是"报文最大生存时间×2",覆盖"发送ACK+等待旧报文过期"两个阶段,是TCP可靠关闭的关键设计。
    补充:实操小知识
  • 查看Linux系统TIME_WAIT默认超时时间: bash cat /proc/sys/net/ipv4/tcp_fin_timeout
    (默认值通常为60秒,即1个MSL,实际2MSL需结合内核参数tcp_msl,默认MSL为30秒);
  • 若服务器频繁出现大量TIME_WAIT连接(如高并发场景),可通过调整内核参数(如tcp_tw_reuse)优化,但需谨慎(可能降低连接可靠性)。

7. 滑动窗口:流量控制的核心

滑动窗口是TCP实现流量控制高效传输 的核心机制,本质是对发送方缓冲区的"动态区域划分 ",既保证不超出接收方的处理能力,又能最大化利用网络带宽,以下从核心概念、移动规则、边界处理、流量控制关联 四个维度梳理:

  1. 滑动窗口的核心背景与本质

    1. 设计目标:

      TCP允许发送方一次性发送多个报文 (而非单报文应答),将"等待多个响应的时间重叠"提升传输效率;
      但需避免发送速率超过接收方的处理能力 ,因此通过"滑动窗口"限制"已发送未确认"的报文总量。

    2. 缓冲区的区域划分
      滑动窗口的本质是发送方的发送缓冲区中的一块数据 。发送缓冲区可类比为"数组",通过两个指针(起始/结束下标)划分出三个核心区域 ,无需额外保存报文,仅通过指针标记区域即可:

      区域类型 含义 缓冲区状态
      已发送已确认 数据已发送且收到接收方ACK应答,可被覆盖/移除(缓冲区可复用) 无效数据,不占用窗口
      已发送未确认 数据已发送但未收到ACK,是滑动窗口的核心范围,需等待应答或超时重传 滑动窗口的实际覆盖区域
      待发送 数据已准备好,但因窗口限制暂未发送,需等窗口滑动后纳入发送范围 窗口外的待发送数据
    3. 滑动窗口的定义
      滑动窗口 = 发送缓冲区中「已发送未确认」的区域
      滑动窗口的大小由接收方的"剩余接收能力"决定 (即接收方ACK报文中的窗口大小(win)),而非发送方自行决定。(在讲完拥塞控制之后会修正)

  2. 滑动窗口的移动规则

    滑动窗口的"滑动"本质是两个指针(start/end)的动态调整 ,核心结论:窗口仅向右移动,左侧(已发送已确认区域)不可回退;窗口大小随接收方能力动态变化

    1. 指针定义(代码层面理解)

      • start(起始指针):对应接收方ACK报文中的确认序号x,标记"已发送已确认"和"已发送未确认"的分界;
      • end(结束指针):start + 接收方窗口大小win,标记"已发送未确认"和"待发送"的分界;
      • 窗口大小 = end - start
    2. 三种移动场景(窗口大小动态变化)

      移动方式 接收方状态 窗口大小变化 核心说明
      左指针移动,右指针不动 接收方上层应用未取数据,接收缓冲区剩余空间减少 变小 接收方处理能力下降,限制发送方发送更多数据
      左指针、右指针同步右移 接收方接收能力提升(缓冲区剩余空间增加) 变大 允许发送方一次性发送更多报文,提升效率
      左指针移动速度 < 右指针 接收方接收能力缓慢下降 动态变小 右指针移速随接收方能力调整,窗口逐步收缩
    3. 关键补充

      • 窗口不能向左移动:左侧是已完成确认的区域,指针回退无意义(已确认数据无需重处理);
      • 窗口大小可变大/变小/不变 :完全由接收方的ACK报文里的win值决定,是"接收方告知发送方自身处理能力"的核心方式。
  3. 窗口越界的处理:环形序号机制

    1. 问题本质

      若窗口持续右移,缓冲区下标/报文序号会耗尽,导致越界;同时需避免"新连接收到旧连接残留报文"的问题。

    2. 解决方案:环形算法+初始序号协商

      • 环形序号 :TCP对报文序号采用"环状算法",序号耗尽后重新从起始位置计算start/end,避免缓冲区下标越界;
      • 初始序号(ISN)协商:三次握手时双方同步初始序列号(非从0开始),即使断开连接后快速重建,新连接的序号与旧连接残留报文的序号不重叠,避免旧数据干扰新连接(配合TIME_WAIT的2MSL等待,进一步降低冲突概率)。
  4. 滑动窗口与流量控制的关联

    TCP的流量控制完全基于滑动窗口实现,核心逻辑:

    1. 流量控制≠"单纯限制发送":若接收方处理能力强(缓冲区剩余空间大),会通过ACK报文中的win值扩大发送方窗口,允许发送更多数据,提升传输效率;
    2. 流量控制的核心目标:让发送方的发送速率匹配接收方的处理速率,既不浪费网络带宽,也不导致接收方缓冲区溢出。

一些问题:

Q:滑动窗口可以向左滑动吗?
A: 不能向左移动,左侧是已经发送已经确认的,只会往右移动。
Q:滑动窗口的窗口大小会发生改变吗?
A: 窗口大小是一直在动态变化的,由网络因素(拥塞控制)和对端接收能力(流量控制)共同影响。
Q:如果传输过程中丢包了怎么办?滑动窗口会不会跳过报文进行应答?
A: 丢包分为两种情况:

  1. 接收端收到数据,发送确认应答后应答丢失,应答没有被发送端收到,导致发送端认为丢包。
  2. 发送端发送的数据真的丢失。

情况一:应答丢失

  1. 对齐颗粒度:

    • TCP滑动窗口的核心优势之一是"支持基于后续ACK的批量确认",即使部分ACK应答丢失,也无需立即重传,可通过后续收到的高序号ACK完成批量确认,大幅降低重传开销。
    • TCP采用累积确认 机制:接收方回复的确认序号N,表示"序号N之前的所有数据已完整接收"(如收到6001的ACK,代表1~6000的报文全部确认)。滑动窗口的start指针直接跟随"最新的有效确认序号"移动,而非逐段等待每一个ACK。
  2. 场景模拟:

场景1: 部分低序号ACK丢失(如3001、5001丢失,收到1001、2001、4001)

  • 接收方回复的ACK:1001(确认1000)、2001(确认2000)、4001(确认4000);
  • 滑动窗口移动逻辑:
    1. 收到1001:窗口start从0→1001,窗口右滑至1001;
    2. 收到2001:窗口start从1001→2001,窗口继续右滑至2001;
    3. 收到4001:因累积确认规则,4001代表1~4000已全部接收(即使3001的ACK丢失),窗口start直接从2001→4001,无需卡在3001等待;
  • 核心结论:低序号ACK丢失不影响窗口滑动,后续高序号ACK可"覆盖确认",窗口直接滑到高序号位置。

场景2: 最高序号ACK丢失(如6001丢失)

  • 发送方已发送1000~6000,仅收到5001的ACK(确认5000),6001的ACK丢失;
  • 滑动窗口移动逻辑:
    1. 窗口start先滑至5001(基于5001的ACK),表示1~5000已确认;
    2. 对于6000的报文,发送方进入"超时等待":若超时前仍未收到6001的ACK,触发超时重传6000报文;
    3. 重传成功并收到6001的ACK后,窗口start滑至6001,完成全部确认。

场景3: 所有ACK全部丢失

  • 发送方发送1000~6000后,未收到任何ACK(1001/2001/3001/4001/5001/6001均丢失);
  • 处理逻辑:
    1. 滑动窗口start始终停留在初始位置(0),无任何右滑;
    2. 每个报文依次触发"超时重传"(或基于快速重传机制),直到发送方收到对应ACK;
    3. 若重传后收到某高序号ACK(如4001),窗口直接滑至4001,剩余未确认报文继续等待/重传。

情况二:数据丢失

场景前提:

发送方按序发送6段报文(序号范围:1 ~ 1000、1001 ~ 2000、2001 ~ 3000、3001 ~ 4000、4001 ~ 5000、5001~ 6000);

接收方仅丢失1001~2000段报文,2001~6000所有报文均正常接收。

快速重传的触发与执行流程

  1. 接收方的应答规则

    接收方因未收到1001~2000,无论后续收到多少段正常报文(2001 ~ 6000),所有应答报文的确认序号均填写1001,表示:"我仅确认收到1 ~ 1000,下一个需要的是1001开始的数据"。

  2. 发送方触发快速重传

    发送方连续收到3个及以上确认序号为1001的重复应答 时,立即判定:1001~2000段报文丢失(无需等待超时),立即触发重传,该重传机制叫"快速重传"

    • 仅重传丢失的1001~2000段报文,2001~6000已确认正常到达的报文不重传;
    • 若丢失的是其他段(如5001~6000),则仅针对性重传该段,不影响已正常到达的报文。
  3. 重传后的确认

    发送方补发1001~2000段报文后,接收方确认该段已收到,此时回复的确认序号变为6001(累积确认规则),表示:"1~6000所有报文已完整接收"。

核心特点总结

  1. 快速重传的触发条件:发送方收到3个相同的重复确认序号 ,无需等待超时;

  2. 重传策略:精准重传丢失的单段报文 ,而非全量重传,大幅减少无效数据传输;

  3. 核心优势:相比"超时重传",快速重传省去了超时等待时间,显著提升丢包后的恢复效率;

  4. 累积确认兜底:重传成功后,接收方通过累积确认直接回复最高序号(6001),完成全量确认。


8. 流量控制

接收端处理数据的速度是有限的, 如果发送端发的太快, 导致接收端的缓冲区被打满。

这个时候如果发送端继续发送, 就会造成丢包, 继而引起丢包重传等等一系列连锁反应。
因此TCP支持根据接收端的处理能力, 来决定发送端的发送速度 。 这个机制就叫做流量控制(FlowControl);

  • 接收端将自己可以接收的缓冲区剩余空间大小放入 TCP 首部中的 "窗口大小" 字段, 通过ACK端通知发送端,动态修改滑动窗口大小,达到流量控制的效果;
  • 窗口大小字段越大, 说明网络的吞吐量越高;
  • 接收端一旦发现自己的缓冲区快满了, 就会将窗口大小设置成一个更小的值通知给发送端;
  • 发送端接受到这个窗口之后, 就会减慢自己的发送速度;
  • 如果接收端缓冲区满了, 就会将窗口置为0; 这时发送方不再发送数据, 但是需要定期发送一个窗口探测数据段, 使接收端把窗口大小告诉发送端。

Q:三次握手成功后第一次发送的时候怎么保证发送数量是合理的?
A: 三次握手不仅仅只是建立链接,三次握手期间双方也是交换了TCP报头了,所以在三次握手期间双方已经协商了双方的接收能力。
Q:第三次握手的时候,可以携带数据吗?
A: 可以的(捎带应答),因为前两次就已经协商好双方的接收能力了,间接证明上一个问题(保证可靠性的时候也提高效率)
Q:流量控制,属于"可靠性",还是属于"效率"?
A: 直接上保证可靠性,因为可以防止大面积丢包,但是流量控制也间接地提高了效率,会让正常报文不会再重传了,超时重传少很多,所以也间接提高了效率。


9. 拥塞控制

TCP拥塞控制是解决"网络层面大量丢包"的核心策略,区别于"少量丢包(单主机/单报文问题)",聚焦"网络共享资源耗尽导致的全局拥塞",以下从场景判断、核心机制、关键规则三个维度完整梳理:

  1. 拥塞的判定:区分"少量丢包"与"网络拥塞"

    1. 场景类比

      • 少量丢包:班级30人考C语言仅2人挂科→单客户端/服务器的局部问题(如单报文传输故障、接收方临时处理能力不足);
      • 大量丢包:考网络28人挂科→网络层面的全局问题(即"网络拥塞"),影响所有基于TCP通信的主机。
    2. 网络拥塞的触发原因

      • 硬件层面:网络设备(路由器、交换机)故障、带宽不足;
      • 流量层面:网络数据吞吐量超出承载上限,引发数据阻塞。
    3. TCP如何识别网络拥塞?

      核心判定依据:滑动窗口内大量数据超时未收到ACK应答 (而非少量报文丢包)。

      → 少量丢包仅触发"快速重传",大量超时则判定为网络拥塞需启动拥塞控制机制

  2. 拥塞控制的核心目标与原则

    1. 核心目标 :避免单主机重传加剧拥塞,通过"多主机共识"减少数据发送,让网络拥塞快速消散(网络资源为所有主机共享,需全局适配);
      (emmm...这里说人话就是:网络的拥塞不是我一个主机造成的,要让网络上的很多主机同时都减少数据发送,才能达到拥塞控制的目的。)
    2. 核心原则
      • 不盲目超时重传:重传会进一步占用网络资源,加重拥塞;
      • 自适应调整:网络拥塞程度动态变化,控制策略需"搏概率"适配(并非所有主机能同时识别拥塞,但识别到的主机需立即降速);
      • 逐步恢复:拥塞缓解后,逐步增加发送量,而非直接恢复满速。
  3. 拥塞控制的核心机制:慢启动(Slow Start)

    1. 核心概念:拥塞窗口(cwnd)

      • 定义:主机判断网络健康程度的核心指标,代表"当前网络可承载的最大发送量",动态变化;

      • 作用:滑动窗口的实际大小由「接收方窗口(16位窗口大小,对方接收能力)」和「拥塞窗口(网络承载能力)」共同决定:

        复制代码
        实际滑动窗口大小 = min(接收方窗口大小, 拥塞窗口大小, 有效数据量)

        → 接收方窗口关注"对方能不能收",拥塞窗口关注"网络能不能传"。
        这里修正了之前的概念。

    2. 慢启动的执行规则

      (1)初始阶段:指数级增长("慢"在起点)

      • 拥塞窗口初始值设为1(仅发送1个报文);
      • 每收到1个ACK应答,拥塞窗口大小×2(指数级增长);
      • 例:cwnd=1→收到ACK→cwnd=2→收到ACK→cwnd=4→收到ACK→cwnd=8...

      (2)阈值限制:指数转线性增长

      • 慢启动阈值(ssthresh):取"最近一次拥塞时拥塞窗口大小的1/2";
      • 当拥塞窗口 ≤ 阈值:保持指数增长,快速恢复通信效率;
      • 当拥塞窗口 > 阈值:停止指数增长,改为线性增长(每次仅增加1),避免窗口过大再次引发拥塞。

      (3)拥塞复发的处理

      若再次检测到拥塞(大量超时):

      • 将慢启动阈值更新为"当前拥塞窗口大小/2";
      • 拥塞窗口直接重置为1,重新执行慢启动流程。
    3. 为什么"指数增长"叫"慢启动"?

      "慢"是相对"拥塞时直接满速发送"而言:

      • 初始阶段仅发送少量报文(cwnd=1),避免刚恢复就加重拥塞,这是"慢"的核心;
      • 指数增长是为了在网络健康时"尽快恢复正常传输效率",而非一直慢;
      • 阈值机制进一步限制了后期增长速度,平衡"恢复效率"与"避免拥塞复发"。
  4. 核心总结

    1. TCP拥塞控制针对"网络全局大量丢包",区别于"少量丢包的快速重传";
    2. 核心判定依据:滑动窗口内大量数据超时,识别为网络拥塞;
    3. 慢启动是拥塞控制的核心机制,通过"拥塞窗口"动态调整发送量:
      • 初始cwnd=1(慢起点),ACK确认后指数增长;
      • 超过阈值后线性增长,拥塞复发则重置cwnd=1、更新阈值;
    4. 实际滑动窗口大小由"接收方能力"和"网络承载能力"共同决定,兼顾对方接收与网络传输的双重限制。


10. 延迟应答

延迟应答是TCP协议层的一种效率优化策略,核心是通过推迟发送ACK应答,让接收方的应用层有更多时间消费缓冲区数据,从而向发送方通告更大的接收窗口,提升整体传输效率。

  1. 设计背景

    发送方一次发送的数据量,由接收方在ACK报文中通告的窗口大小 决定。窗口越大,发送方一次能发的数据就越多,IO次数越少,传输效率越高。

    如果接收方立即应答,可能因为应用层还没来得及取走数据,导致返回的窗口较小;

    而延迟应答能给应用层留出时间,让缓冲区释放更多空间,从而通告更大的窗口。

  2. 延迟应答的执行逻辑

    1. 触发时机:接收方收到报文后,TCP协议层不立即发送ACK,而是在不超时的前提下(通常200ms内)等待后续报文。
    2. 等待目的:在等待期间,上层应用有更大概率从接收缓冲区中取走数据,使缓冲区剩余空间变大。
    3. 应答优化:当收到第2~3个报文或等待超时后,再发送ACK,并携带此时更大的窗口大小。

    示例:1M接收缓冲区场景

    • 立即应答 :收到500K数据后立刻回复ACK,此时缓冲区剩余500K,因此通告窗口为500K
    • 延迟应答 :等待200ms后再应答,若应用层已将500K数据消费完毕,缓冲区剩余1M,此时通告窗口为1M

    发送方收到更大的窗口后,就能一次性发送更多数据,减少IO次数,提升传输效率。

  3. 核心特点

    1. 概率性优化 :仅当应用层能快速消费数据时,延迟应答才能提升效率;
      如果上层一直不取数据,缓冲区剩余空间不会增加,反而会因延迟应答增加往返时间,降低效率。
    2. 超时保障:延迟应答有严格的超时限制(通常≤200ms),避免因过度延迟导致发送方超时重传,引发不必要的网络开销。
    3. 双向适用:客户端与服务器均可启用延迟应答,双方的滑动窗口都能因此受益。
  4. 总结

    延迟应答是TCP通过"时间换空间"提升传输效率的典型策略:

    • 核心是利用等待时间让应用层消费数据,从而通告更大的接收窗口;
    • 本质是在"提升发送效率"与"避免超时重传"之间做平衡,是一种概率性的效率优化手段。

11. TCP通信常见异常情况及处理机制

TCP作为面向连接的可靠协议,针对进程终止、机器重启、机器掉电/网线断开等典型异常场景,设计了不同的处理逻辑,同时结合协议层保活机制与应用层检测策略,保障连接的有效性,以下是各异常场景的具体处理方式:

  1. 进程终止
    进程终止时,操作系统会自动释放该进程占用的所有文件描述符,

    对应TCP连接会由内核触发正常的关闭流程,主动向对端发送FIN报文 ,后续按TCP四次挥手的标准步骤完成连接关闭,与应用层主动调用close()的正常关闭逻辑完全一致,对端可感知到连接的正常终止。

  2. 机器重启

    机器重启的处理逻辑与进程终止完全相同:重启过程中系统会回收所有进程资源、释放文件描述符,内核会为当前存在的TCP连接发送FIN报文,按四次挥手完成正常关闭,对端接收FIN后,按标准流程处理连接关闭,无额外异常交互。

  3. 机器掉电/网线断开

    该场景下,断连方无法主动向对端发送任何报文(FIN/RST),对端初期会认为TCP连接仍处于正常的ESTABLISHED状态,后续通过TCP协议层保活机制应用层检测策略发现并处理断连,具体流程:

    1. 若对端有写入操作:对端向断连方发送数据报文后,始终无法收到ACK应答,内核检测到连接异常,会向断连方发送RST报文,强制终止当前连接,释放本地连接资源;
    2. 若对端无写入操作:TCP协议层内置保活定时器,会按固定时间间隔向对端发送保活探测报文,若多次探测均无响应,判定对方已断连,主动释放当前连接;
    3. 应用层兜底:除TCP协议层的保活机制外,多数基于TCP的应用层协议会设计专属的状态检测策略,补充实现断连感知:
      • HTTP长连接:会定期发送探测包检测对端状态,若探测失败则主动关闭连接;
      • 即时通讯类(如QQ):断线后会启动定时重连机制,持续尝试与服务端建立新连接,恢复通信。

相关推荐
独自归家的兔6 小时前
Ubuntu 系统 systemd timers 详解:替代 crontab 的定时任务进阶方案
linux·运维·ubuntu
Lsir10110_6 小时前
【Linux】深入解剖页表——分页式存储
linux·运维·服务器
爱吃生蚝的于勒6 小时前
【Linux】线程概念(一)
java·linux·运维·服务器·开发语言·数据结构·vim
fengyehongWorld6 小时前
Linux yq命令
linux·运维·服务器
岁岁种桃花儿6 小时前
Flink从入门到上天系列第一篇:搭建第一个Flink程序
大数据·linux·flink·数据同步
_OP_CHEN6 小时前
【Linux系统编程】(二十九)深度解密静态链接:从目标文件到可执行程序的底层魔法
linux·操作系统·链接·文件系统·c/c++·静态链接
RisunJan6 小时前
Linux命令-lprm(删除打印队列中任务)
linux·运维·服务器
云姜.6 小时前
TCP协议特性
服务器·网络·tcp/ip
zzzsde6 小时前
【Linux】进程(5):命令行参数和环境变量
linux·运维·服务器