三亩地 三亩地SAN MU DI · CODE DIARY
ARTICLE DETAIL

日记详情

真实记录编程学习的某一天,欢迎挑你感兴趣的翻一翻。

网络(五)|别再死背三次握手!一文吃透 TCP 全底层原理

网络(五)|别再死背三次握手!一文吃透 TCP 全底层原理

传输层

  • 前言
  • 传输层
  • UDP VS TCP
    • 四大核心特性对比
    • 端口号详解
  • UDP协议深度解析
    • UDP报文头部格式
    • UDP 64KB 上限痛点
    • 校验和(Checksum)作用
    • UDP 四大核心特点
    • 基于UDP的经典应用层协议
  • TCP 协议深度解析
    • TCP 报文头部格式
    • TCP 四大核心特性
    • TCP 可靠传输核心机制
      • 确认应答(ACK)—— 最核心机制
      • 超时重传 —— 解决丢包问题
      • 连接管理 —— 建立连接+断开连接
        • TCP 的三次握手
        • TCP 的四次挥手
      • 滑动窗口
      • 流量控制
      • 拥塞控制
      • 延时应答
      • 捎带应答
      • 面向字节流
    • UDP 与 TCP 取舍总结

前言

上一篇我们完整梳理了网络层核心知识,明白了 IP 地址、路由、NAT 如何实现跨主机、跨网段的数据包转发,网络层仅能把数据送达目标主机。

但一台设备上会同时运行浏览器、服务端程序、数据库等多个进程,只依靠 IP 无法精准区分数据该交给哪一个程序处理,这就需要传输层完成端到端进程通信。

本篇为后端面试最高频重难点,完整覆盖两大核心协议 UDP、TCP,包含端口作用、TCP 报文结构、三次握手 / 四次挥手、可靠传输机制、流量 / 拥塞控制、粘包拆包等必考内容。
承接网络层 “主机寻址”,延伸到传输层 “进程寻址”,为后续应用层 HTTP、Socket 网络编程打下核心理论基础。

传输层

网络层只负责把包送到目标主机;传输层负责精准送到主机里对应的进程,提供端到端通信。
两个核心协议:TCP、UDP。

UDP VS TCP

四大核心特性对比

特性UDPTCP
连接属性无连接有连接(三次握手建立连接)
传输可靠性不可靠传输可靠传输
传输单位面向数据报(DatagramPacket)面向字节流
通信模式全双工全双工

端口号详解

端口号是区分一台主机上不同应用程序的标识,固定为2字节(16位),取值范围为0-65535。

  1. 服务器:必须手动指定固定端口号,客户端通过IP+固定端口访问服务;
  2. 客户端:由操作系统自动分配随机空闲端口,无需手动指定;
  3. 端口分段:
    (1)1-1023:知名端口号,系统预留,绑定知名服务(22 SSH、80 HTTP、443 HTTPS);
    (2)1024-65535:普通端口号,业务开发自定义端口。

注意:80 只是 HTTP 协议建议端口,不是强制端口;开发时务必保证自定义端口不被其他程序占用。

UDP协议深度解析

UDP报文头部格式

UDP 报文 = 8字节固定头部+应用层载荷数据,头部分为4段,每段2字节:

  1. 源端口号;
  2. 目的端口号;
  3. UDP 报文总长度(头部+载荷);
  4. UDP 校验和(checksum)。

源IP、目的IP不属于UDP头部,存储在下层网络层IP协议头部;
报文长度16位,最大值65535,因此单个UDP数据包最大只能携带64KB数据。

UDP 64KB 上限痛点

网页、大文件等场景下,业务数据很容易超过 64KB:
方案一:手动拆分数据包、接收端重组,开发和测试成本极高;
方案二:直接改用 TCP,TCP 没有单包大小限制。

校验和(Checksum)作用

网络传输会受电磁干扰、线路波动,出现比特翻转、数据错乱,校验和用来校验数据是否传输出错。
校验和,本质上也是一个字符串,体积比原始数据更小,又是通过原始的数据生成的:

  1. 原始数据相同,得到的校验和就一定相同;
  2. 校验和相同,原始数据大概率相同。

如何基于校验和来完成数据校验呢?

  1. 发送方:根据原始数据(data1),通过算法计算出校验和;
  2. 发送方同时把原始数据 data1 + 校验和 checksum1 一起通过网络发送出去;
  3. 接收方:用相同算法、重新根据收到的数据(data2 + checksum1)计算新校验和 checksum2;
  4. 对比新旧校验和:不一致代表数据损坏,直接丢弃数据包;一致代表大概率传输正常。

计算校验和的算法有哪些?

  1. CRC 循环冗余算法:UDP 默认使用,存在哈希碰撞,不同数据可能算出相同 CRC;
    把当前要计算校验和的数据,每个字节都进行累加,把结果保存到这个两个字节的变量中,累加过程若发生溢出也无妨;若中间某个数据出现传输错误,第二次计算的校验和会和第一次不同。
  2. MD5/SHA 哈希算法:
    (1)定长输出:无论原始数据多长,计算得到的 MD5 结果固定16字节;
    (2)雪崩效应:原始数据只有1bit不同,最终哈希值差异巨大;
    (3)不可逆:无法通过哈希值反推原始数据,常用于密码加密、数据完整性校验。

UDP 四大核心特点

  1. 无连接:协议本身不记录对端地址,每次发送数据包,都必须手动携带目标IP+端口;
  2. 不可靠传输:没有重传、确认机制,丢包、乱序都不会处理;
  3. 面向数据报:以完整 DatagramPacket 数据报为最小传输单位,不会拆分、拼接;
  4. 全双工:同一个 DatagramSocket 对象,既可以调用 send 发送,也可以调用 receive 接收。

基于UDP的经典应用层协议

  1. DNS:域名解析协议;
  2. DHCP:动态主机配置协议,自动分配 IP 地址;
  3. TFTP:简单文件传输协议;
  4. NFS:网络文件系统;
  5. BOOTP:无盘设备启动协议。

TCP 协议深度解析

TCP 报文头部格式

TCP 报文 = 可变头部 + 载荷数据;

  1. 头部最小20字节,最大60字节;多出的40字节属于选项字段;
  2. 核心字段:16位源端口、16位目的端口、32位序号、32位确认序号、6个标志位、窗口大小、16位校验和等。

TCP 四大核心特性

  1. 有连接:通信前通过三次握手建立专属连接,通信结束四次挥手断开连接;
  2. 可靠传输:保证数据不丢失、不乱序、不重复;
  3. 面向字节流:把所有数据当作连续字节流处理,没有数据包边界;
  4. 全双工:一条连接可同时双向收发数据。

TCP 可靠传输核心机制

确认应答(ACK)—— 最核心机制

发送方把数据发给接收方之后,接收方收到数据就会给发送方返回一个应答报文(acknowledge;ACK 的确认序号 = 收到数据最后一个字节序号 + 1),此时发送方收到这个应答报文,就能够知道自己的数据是否发送成功。

由于不同的数据包可能会走不同的路线,实际中,网络传输数据还可能会出现“后发先至”的情况。

TCP 在此处要完成两个工作:

  1. 确保应答报文和发出去的数据能对上号,不要出现歧义;
  2. 确保在出现“后发先至”现象时,能够让应用程序仍然按照正确的顺序来理解数据(依靠32位序号+32位确认序号,描述数据的先后顺序)。

如何区分一个数据包,是普通的数据包还是应答数据包?

URGACKPSHRSTSYNFIN

有六个标志位,判断应答包,只看ACK位,ACK=1代表当前报文是应答报文,此时该数据包中的“确认序号字段”生效。

TCP是如何保证可靠传输的?
TCP以确认应答为基础,配合超时重传、快速重传、序号去重等一系列机制,共同实现可靠传输。

超时重传 —— 解决丢包问题

丢包:若数据包太多,就会在路由器/交换机上出现“堵车”,而路由器针对这一现象的处理就是把积压的数据包中的大部分之间丢弃掉。

网络拥堵、线路故障会引发数据包丢失,分为两种场景

  1. 发送的数据包本身丢了;
  2. 接收方返回的ACK应答报文丢了。

两种情况都会导致发送方超时没有收到ACK,触发超时重传

  1. 发送方发送数据后开启等待计时器;
  2. 超时前收到ACK,重置计时器;
  3. 超时未收到ACK,立刻重传当前数据包。

超时等待时间动态变化:每次超时重传,等待时间都会翻倍;
多次重传依旧失败,判定网络严重故障,触发 TCP 连接重置;
代价:可靠传输牺牲了一部分传输效率,这也是 UDP 无法被 TCP 完全取代的原因。

接收缓冲区,不仅仅能够进行去重,还能进行重新排序。确保发送的顺序和应用程序读取的顺序是一致的。

连接管理 —— 建立连接+断开连接

建立连接就是三次握手的过程。
断开连接就是四次挥手的过程。
TCP此处的握手,就是给对方传输一个简短的、没有业务数据的数据包,通过这个数据包先唤醒对方的注意,再进行后续操作。

TCP 的三次握手

TCP 在建立连接过程中,需要通信双方一共握手三次才能完成连接的建立。
实际开发中,主动发起的一方,就是“客户端”,被动接受的一方,就是“服务器”。

  1. 建立连接的过程,其实是,通信双方都要给对方发起SYN,也要给对方反馈ACK。
  2. TCP初心是为了实现可靠传输,能够进行确认应答和超时重传有个前提,当前网络应该是基本可用的、通畅的。

三次握手的核心作用

  1. 确认当前网络是否通畅
  2. 要让发送方和接收方都能确认自己的发送能力和接收能力的正常
    (1)A发送SYN;
    (2)B发送ACK和SYN,此时B确定A的发送能力以及自己的接收能力正常;
    (3)A发送ACK,此时A确定自己以及B的发送能力和接收能力均正常,发送ACK,告知B。
    三次握手很重要,四次握手也可以,但没必要,二次握手不可以,达不到要求。
  3. 让通信双方在握手过程中针对一些重要的参数进行协商
    TCP通信过程中的序号从几开始,就是双方协商出来的。
    每次建立连接时,都会协商出一个和上次不一样的值。

TCP的两个重要状态

  1. LISTEN:服务器端的状态。
    服务器Socket创建好并并把端口号绑定好,进入LISTEN状态,允许客户端随时来建立连接。
  2. ESTABLISHED客户端、服务器都会有的状态。
    连接建立完成,接下来可以进行正常通信了。
TCP 的四次挥手

建立连接,一般是客户端主动发起的;
断开连接,客户端和服务器都可以主动发起。


连接断开,也就是A和B把对端的信息删掉。

FIN会在Socket对象close时被发起:手动调用close方法、进程结束。

注意,此处B端的ACK和FIN通常不能合并,因为两者之间的时间差不能确定,而TCP还有一个延时应答机制,能够拖延ACK的回应时间,延时应答的时间窗口恰好合适时,可以合并为三次挥手。

TCP的两个重要状态

  1. CLOSED:连接已经彻底断开,可以释放。
  2. TIME_WAIT:哪一方主动断开连接,哪一方就会进入TIME_WAIT状态,等待时长为 2MSL,目的是为了防止最后一个ACK丢失。在上图的过程中,A端会进入TIME_WAIT状态。

以上的三个机制都是在保证TCP的可靠传输。

滑动窗口

滑动窗口是为了提高TCP的传输效率。
TCP的可靠传输是会影响传输效率的。=> 引入可靠性的TCP的效率不可能超过无可靠性的UDP。

滑动窗口:缩短确认应答的等待时间。

滑动窗口的滑动机制:

从图中不难看出:窗口越大,等待的ACK越多,传输效率就越高。

但上述滑动窗口出现了丢包怎么办?

  1. ACK丢了
    不需要任何重传,只需看确认序号(当前序号之前的数据已经确认收到了,下一个应该从确认序号这里继续发送)。
    若ACK全丢了,丢包率就是100%。
  2. 数据包丢了

    上述重传过程,是哪个数据丢了,就重传哪个,没丢的数据不需要重传。=>快速重传
    该重传机制就是快速重传,滑动窗口只是发送模式,本身不提供重传能力

那滑动窗口是设置的越大越好吗?

不是。若传输的速度太快,接收方就会处理不过来,会出现丢包,发送方还要重传。

  1. 通信双方,传输的数据量比较小,也不频繁时,使用普通的确认应答机制+超时重传。
  2. 通信双方,传输的数据量比较大,比较频繁时,使用滑动窗口+快速重传。

流量控制

流量控制就是站在接收方的角度,反向制约发送方的传输速率。
发送方发送的速率不应该超过接收方的处理能力。

  1. 发送发送的数据会先到达接收方的接收缓冲区,接收方从接收缓冲区中读取数据。
  2. 通过接收方缓冲区剩余空间的大小来作为衡量处理能力的指标。
  3. 接收方每次收到数据之后,都会把接收缓冲区剩余空间的大小通过ACK返回给发送方。
  4. 当接收方的缓冲区满了,发送方会暂停发送数据,但会周期性发送一个“窗口探测包”,并不携带具体的业务数据,只是为了触发ACK,查询当前接收方的接收缓冲区剩余空间的大小。

拥塞控制

  1. 整个通信的路径也会影响传输效率。中间的转发过程中任意一个节点的处理能力达到上限,都可能对发送方产生影响,都可能会影响可靠传输。
  2. 因此,还需要衡量通信过程中的中间节点情况。
  3. 接收方的处理能力很方便进行量化(通过接收缓冲区空间剩余大小),但中间节点结构很复杂,因此可以通过“实验”找到一个合适的值。
  4. “实验”过程:把中间节点视为一个整体,发送发首先按照比较低的速度发送数据(小窗口),若传输过程很顺利,没有出现丢包现象,就尝试使用更大的窗口、更高的速度进行数据传输。如此反复,随着窗口大小不停增大,达到一定程度,可能中间节点就会有一些达到最大限度,此时该节点会出现丢包现象,发送方若发现丢包现象出现了,就把窗口大小调整小一点,此时若继续丢包就继续缩小,若未丢包,就继续变大。
  5. 在整个过程中,发送方不停调整窗口大小,逐渐达成“动态平衡”。

    刚开始,会发送的很慢(慢开始/慢启动),一开始是指数增长,增长到阈值,就会变为线性增长,到一定程度就会出现丢包,一旦触发丢包,就把窗口大小缩小,重新开始,并且根据当前丢包的窗口大小指定一个新的线性增长阈值。
    慢开始:出现丢包后,缩小窗口,重新进行慢开始 -> 指数增长 -> 线性增长。
    快恢复:出现丢包后,缩小窗口到新阈值,跳过指数增长过程,直接开始线性增长。

流量控制和拥塞控制都在限制发送方的发送窗口大小,最终取两者的最小值。

延时应答

  1. 延时应答,是为了提升传输效率。
  2. 发送方的窗口大小是传输效率的关键。延时返回ACK,给接收方更多的时间来读取接收缓冲区的数据。此时接收方读取了更多的数据之后,缓冲区就有了更大的剩余空间,返回的窗口大小也就变大了。

捎带应答

捎带应答,就是在延时应答的基础上进一步提高效率。
网络通信中,往往是“一问一答”通信模型。

面向字节流

  1. 面向字节流的机制都会有一情况:粘包问题。
  2. 若同时有多个应用层数据包被传输过去,此时就容易出现粘包问题。
  3. 像UDP这种面向数据报的通信方式就没有上述问题。
    UDP接收缓冲区中,是一个个DatagramPacket对象。
  4. 如何解决粘包问题?
    核心思路:通过定义好应用层协议,明确应用层数据包之间的边界。=> 引入分隔符(比如:aaa\n、bbb\n)或引入长度(比如:3aaa、3bbb)。

UDP 与 TCP 取舍总结

  1. TCP 适用场景:文件下载、网页浏览、数据库交互、支付业务。场景要求数据绝对完整,丢失数据包必须重传补齐。
  2. UDP 适用场景:直播、语音通话、游戏对战、实时视频。这类场景允许少量丢包,优先降低延迟、保证传输速度;如需可靠性需要上层应用自己实现重传逻辑。

到此传输层 TCP 与 UDP 的核心原理就讲解完毕。传输层向下依托网络层完成主机转发,向上为应用层提供进程通信能力。下一篇,我们将进入应用层,详解 HTTP 与 HTTPS 协议。

本篇是「Java后端编程系列」的连载内容,点击链接查看完整系列:

🔹 上一篇:网络(四)|全网最细网络层原理:IP 协议、IP 地址分类、子网划分、路由转发、NAT 穿透详解

👉 点击直达 Java后端基础专栏合集

← 返回列表