TCP 核心知识精讲:是什么 · 为什么 · 怎么做
用电话类比、银行重复扣款案例、"前朝的剑斩今朝的官"比喻,带你从根本逻辑理解 TCP 的每一个设计。
目录


引言
TCP 是互联网的基石,它的每一个机制都源于一个简单的追问:如何在不可靠的 IP 网络上构建一个可靠的、有序的、无差错的传输服务?
本文将从 "是什么、为什么、怎么做" 三个维度,系统拆解 TCP 的核心知识点,让你不仅记住概念,更能理解背后的设计思想。
一、TCP 报头各属性:是什么、为什么、怎么做
1. 源端口 & 目的端口
-
是什么
TCP 头部中两个 16 位字段,用于标识发送端和接收端的应用程序。
-
为什么
🔥 一台主机上可能同时运行多个网络应用(浏览器、微信、游戏)。数据到达时,操作系统必须知道该交给哪个进程。
-
怎么做
目的端口号告诉操作系统将数据交付给哪个进程;源端口号让对方知道回复时应发往哪个端口。
2. 序列号(Seq) & 确认号(Ack)
-
是什么
- 序列号:32 位,本报文段所携带数据的第一个字节的编号。TCP 为每个字节编号。
- 确认号:32 位,期望收到对方下一个报文段的第一个字节序号,表示之前的数据已成功接收。
-
为什么
⚠️ 网络可能丢包、乱序、重复。接收方需要知道数据是否完整、顺序是否正确;发送方需要知道对方收到了哪些数据,以便决定重传或继续发送。
-
怎么做
- 发送方按字节递增序列号。
- 接收方用确认号告知 "我已收到该序号之前的所有数据"。
- 发送方根据确认号释放已确认的数据,并在超时后重传未确认的数据。

3. 标志位(SYN、ACK、FIN、RST、PSH、URG)
-
是什么
每个标志位占 1 位,用于控制连接状态或特殊传输行为。
-
为什么
TCP 不仅传输数据,还要管理连接(建立、维持、关闭、异常)。需要一种方式在不干扰数据流的情况下传递控制信息。
-
怎么做
- SYN:同步序列号,用于建立连接。
- ACK:确认字段有效,表明确认号有意义。
- FIN:请求终止连接。
- RST:强制复位连接(如端口未监听或连接异常)。
- PSH:推送,要求接收方尽快将数据交给应用层。
- URG:紧急指针有效,用于紧急数据。

4. 窗口大小
-
是什么
16 位字段,表示接收端当前可用的接收缓冲区大小(以字节为单位)。
-
为什么
✅ 发送方若发送太快,接收方缓冲区会溢出,导致数据丢弃;若发送太慢,则浪费带宽。需要一种动态调速机制。
-
怎么做
接收方在每个确认中告知自己的窗口大小。发送方根据此值限制未确认的数据量,实现 流量控制。
5. 校验和
-
是什么
16 位字段,覆盖 TCP 头部和数据的校验值。
-
为什么
🔍 数据在传输过程中可能因硬件故障、信号干扰等原因发生比特错误,需要检测并丢弃错误报文。
-
怎么做
发送方计算整个 TCP 报文(包括头部和数据)的校验和,填入头部。接收方重新计算,若不一致则丢弃该报文。
6. 数据偏移 & 选项
-
是什么
- 数据偏移:4 位,表示 TCP 头部的长度(以 4 字节为单位)。
- 选项:可变长度,用于携带可选功能,如 MSS、窗口扩大因子、时间戳、SACK 等。
-
为什么
标准头部为 20 字节,但某些场景需要额外功能(如告诉对方最大报文长度、支持时间戳等),需要一种扩展机制。
-
怎么做
数据偏移字段指示头部实际长度,选项字段按需添加,接收方根据数据偏移解析。
二、三次握手:是什么、为什么、怎么做
1. 是什么
三次握手是 TCP 建立连接时,客户端与服务器交换三个报文段的过程:
- 客户端发送 SYN=1, Seq=x(SYN_SENT)
- 服务器回复 SYN=1, ACK=1, Seq=y, Ack=x+1(SYN_RCVD)
- 客户端发送 ACK=1, Seq=x+1, Ack=y+1 (ESTABLISHED)
服务器收到后也进入 ESTABLISHED,连接建立。
📷 图片预留位置:三次握手状态变迁图(标注 SYN_SENT, SYN_RCVD, ESTABLISHED)。
2. 为什么
-
为什么需要握手?
🔗 在不可靠的网络上,双方需要确认彼此都能正常收发数据,并同步初始序列号,否则后续数据传输无法保证可靠。
-
为什么不能两次?
❌ 两次握手会导致"已失效的连接请求"占用服务器资源。如果只有两次握手,一个过期的 SYN 报文可能延迟很久后到达服务器,服务器会误以为客户端想建立新连接而分配资源,但客户端实际已放弃,导致服务器资源浪费(半开连接)。
-
为什么不用四次?
⏱️ 四次握手(SYN → ACK → SYN → ACK)多一次往返,效率低,而三次握手已经能同时完成双方序列号同步和确认,足够可靠。

3. 怎么做
电话类比:三次握手的功能测试
实际发送的报文内容如上所述,关键是通过序列号和标志位确保双方状态一致。用打电话的过程来类比,每一步都在测试双方的收发能力:
| 步骤 | 操作 | 报文类型 | 测试了什么 | 状态/结论 |
|---|---|---|---|---|
| 第一次 | 你拨通电话,说:"喂!" | SYN | 测试 你的发送功能 和 朋友的接收功能 | 朋友(接收方)知道自己接收正常,你 (发送方)的发送正常 |
| 第二次 | 朋友回应:"我听到了,你能听见我吗?" | SYN+ACK | ① 确认第一次测试成功 ② 测试 朋友的发送功能 和 你的接收功能 | 接收到信息后,你 (发送方)知道自己接收发送和朋友的接收发送都正常 但朋友还不知道自己的发送是否真的被对方听到 |
| 第三次 | 你回答:"我也听到了!" | ACK | 告诉朋友:你的发送功能我也能正常接收 | 朋友得知自己的发送功能正常 至此,双方确认彼此收发能力全部正常 |
为什么两次不够?
如果只有前两次握手就认为通话建立(你拨号 → 朋友接听并说"喂"),可能出现以下问题:
| 场景 | 问题 |
|---|---|
| 你 (发送方)拨号后已挂断 (断开连接) | 朋友还在电话那头空等,不知道你已离开 |
| 朋友 (接收方)在"喂!"了之后迟迟等不到你的回应 | 朋友以为你想通话,实际上你早已放弃 |
两次握手的风险:接收方(朋友)无法确认自己发出的信息(第二次握手)是否被发送方(你)收到,可能导致 半开连接、资源浪费。
通过这个表格,每一步的功能测试目标、状态变化都清晰呈现,三次握手的必要性也就一目了然了。
三、可靠传输机制:是什么、为什么、怎么做
1. 确认应答
-
是什么
接收方收到数据后,返回一个 ACK 报文,确认号 = 期望收到的下一个字节的序号。
-
为什么
💡 发送方需要知道哪些数据已被对方成功接收,以便释放缓存并决定后续发送。
-
怎么做
发送方记录已发送但未确认的数据。收到 ACK 后,移动窗口,继续发送新数据。确认是 累积的:确认号 N 表示序号 N 之前的所有数据都已收到。

2. 超时重传
-
是什么
发送方为每个发送的报文段设置一个重传计时器,若超时未收到 ACK,则重传该报文段。
-
为什么
⚠️ 报文可能在网络中丢失,或者 ACK 丢失,发送方需要一种兜底机制确保数据最终到达。
-
怎么做
重传超时时间(RTO)根据网络往返时间(RTT)动态调整(如 Karn 算法、Jacobson 算法)。若超时,重传最早未确认的报文段。

3. 快速重传
-
是什么
当发送方收到三个重复的 ACK 时,立即重传丢失的报文段,不等超时。
-
为什么
🚀 超时重传反应较慢,快速重传能更快地恢复丢包。
-
怎么做
接收方收到失序报文时,会重复确认期望的序号。发送方连续收到三个相同确认号,即推断该报文段丢失,立即重传。

四、乱序处理(后发先至):是什么、为什么、怎么做
1. 是什么
TCP 使用序列号对收到的数据进行排序,将乱序到达的报文段放入重排序缓冲区,按序交付给应用层。
2. 为什么
📦 IP 网络是"尽力而为"的,数据包可能通过不同路径传输,导致后发的报文先到,先发的后到。如果不处理乱序,应用层将收到混乱的数据流。
3. 怎么做
- 接收端维护一个接收窗口,其中包含一个重排序缓冲区。
- 当收到一个报文段时,根据其序列号放入缓冲区相应位置。
- 只有当数据连续时,才将数据向上交付,缺失的部分暂存等待重传。
4.接亲车队与 TCP 乱序处理:一个生动的比喻
在接亲车队中,有一个常见的现象:载着新娘子的车,往往不是第一个到达男方家的。这听起来有点奇怪,但背后却藏着与 TCP 乱序处理异曲同工的智慧。
车队出发:有序但脆弱
一开始,接亲车队整整齐齐地排成一列,头车是新娘子所在的车,后面跟着亲友的车队。大家约定好路线,一起出发。然而,现实总是不那么理想------路途中可能遇到 堵车、红绿灯、道路施工,甚至有的司机为了赶时间,擅自走了小路。
乱序到达:第一个到的不是新娘子
于是,可能出现这样的情况:某个走小路的司机,因为绕开了拥堵,比头车更早到达男方家。如果他直接开进家门,大家就会发现:"哎?新娘子呢?怎么先来的不是她?" 这显然不符合接亲的规矩------新娘子必须是第一个从车上走下来的人。
约定:村口等待,重新排序
为了解决这个问题,接亲队伍约定了一个规则:所有车辆不得直接进村,必须在村口集合。村口就像一个 缓冲区(缓冲队列) ,所有到达的车辆都先在这里停下来,等待整个车队到齐。
- 如果先到的车(比如走小路的)到了村口,就暂时停着,不进去。
- 等到头车(新娘子车)也到了,大家再按照原来的顺序重新排列,然后一起进村。
- 这样,新娘子就能稳稳当当地从第一辆车下来,完美符合传统。
与 TCP 乱序处理的对应
这个"村口等待"的机制,与 TCP 处理乱序数据的方式如出一辙:
| 接亲车队 | TCP 机制 |
|---|---|
| 车队车辆按顺序出发 | 发送方按序列号顺序发送数据 |
| 道路拥堵、走小路导致车辆乱序到达 | IP 网络导致数据包乱序到达 |
| 村口缓冲区 | 重排序缓冲区(Reordering Buffer) |
| 所有车辆到齐后重新排队进村 | 接收方按序列号排序后,再按序交付给应用层 |
| 可以知道哪些车还没到(比如事故) | 通过序列号检测缺失报文,等待重传 |
延伸:检测"丢失的车辆"
如果在村口等了很久,发现某辆车迟迟未到,大家就知道它可能半路出了事故或迷路了。于是,有人会打电话联系,甚至派车去寻找------这就像 TCP 的超时重传:当发现某个序列号的数据迟迟未到,接收方不会干等,而是通过重复确认(三个重复 ACK)或超时机制,让发送方重传丢失的数据。

五、序列号深度解析:是什么、为什么、怎么做
1. 序列号必须单调递增
-
是什么
序列号在连接建立时随机初始化,但之后随着发送的字节数严格递增。
-
为什么
🔢 如果序列号随意跳跃,接收方无法通过"期望的序号"判断数据是否连续,也无法准确确认丢失或重复。
-
怎么做
每发送一个字节,序列号加 1。接收方根据期望序号识别缺失,发送方根据确认号释放已确认数据。
2. 为什么 32 位(4 字节)足够检测丢包?
-
是什么
32 位序列号范围 0 ~ 4,294,967,295,约 43 亿。
-
为什么
🌐 网络带宽很高时,序列号可能快速回绕。如果旧报文与新连接的报文序号相同,可能导致误判。
-
怎么做
- 配合 MSL(最大报文生存时间):连接关闭后等待 2MSL 才重用端口,确保旧报文在网络中消失。
- 使用 时间戳选项:报文携带发送时间戳,接收方可拒绝明显过旧的报文,即使序号回绕也能区分新旧。
- 现代高速网络中,TCP 时间戳选项已成为标配,解决了回绕问题。
3. 为什么"前朝的剑斩今朝的官"?如何防止?
-
是什么
这是一个形象的比喻:一个已关闭的旧连接,其延迟的报文在新连接建立后到达,如果该报文的序列号落在新连接的窗口内,会被误认为是新连接的有效数据,导致数据错乱或状态异常。
-
为什么
🚨 网络延迟可能导致旧连接的报文残留,若没有防护,会破坏新连接的独立性,甚至引发安全漏洞。
-
怎么做
- 随机初始化序列号(ISN):每次新建连接时,双方随机选择初始序列号,使得旧报文几乎不可能落在新连接的合法窗口内。
- 时间戳选项:接收端检查时间戳,拒绝过旧的报文。
- 2MSL 等待:连接关闭后,必须等待 2MSL 才能重用相同的端口对,确保旧报文完全消失。
-
辫子比喻
🧠 清朝人(旧连接)留着长辫子(旧序列号范围),民国(新连接)要求剪辫子(随机序列号)。若清朝人突然出现在民国,一看辫子就知道是旧时代的人,不予接纳。而且必须等足够久(2MSL)才能改头换面进入新社会。

六、去重机制:是什么、为什么、怎么做(银行重复扣款案例)
1. 传输层去重
-
是什么
TCP 通过序列号识别重复报文:收到相同序列号的报文段时,直接丢弃,不向上交付。
-
为什么
🛡️ 网络可能产生重复报文(如超时重传导致),若不去重,应用层会收到重复数据,导致业务错误。
-
怎么做
接收端维护一个已接收序列号的范围,对重复报文直接丢弃,保证向上交付的字节流无重复。

2. 应用层幂等
-
是什么
即使 TCP 传输层已经去重,业务系统仍需设计幂等性:同一个业务请求无论执行多少次,结果与执行一次相同。
-
为什么
💰 客户端可能因超时、用户重试等原因,在应用层再次发起相同请求(例如重新点击转账按钮)。TCP 去重只针对单个连接内的重复报文,无法处理跨连接、跨时间的重复业务请求。
-
怎么做
- 为每个请求分配全局唯一 ID(如交易流水号)。
- 服务端记录已处理过的 ID,收到重复请求时直接返回原结果,不重复执行业务逻辑。
- 数据库使用唯一约束防止重复插入。
3. 银行重复扣款案例
-
是什么
用户转账 1000 元,服务器扣款成功但 ACK 丢失,客户端以为失败而重试,若无幂等则扣款两次。
-
为什么
🔴 资金安全是金融系统的底线,重复扣款会引发用户投诉、账务不平,甚至法律风险。
-
怎么做
银行系统使用交易流水号作为幂等键,无论重试多少次,同一笔交易只执行一次扣款。数据库层面使用唯一索引,确保重复请求不会产生两条交易记录。

七、总结
TCP 的每一个设计都遵循"问题 → 重要性 → 解决方案"的逻辑:
| 知识点 | 是什么 | 为什么 | 怎么做 |
|---|---|---|---|
| 端口 | 标识应用 | 区分多应用 | 源端口 + 目的端口 |
| 序列号/确认号 | 字节编号与确认 | 实现可靠、有序、去重 | 单调递增 + 累积确认 |
| 标志位 | 连接控制 | 管理连接状态 | SYN、ACK、FIN 等 |
| 窗口大小 | 流量控制 | 防止接收方溢出 | 接收窗口通告 |
| 校验和 | 数据完整性 | 检测比特错误 | 发送计算,接收验证 |
| 三次握手 | 建立连接 | 同步序列号,防历史连接 | SYN → SYN+ACK → ACK |
| 确认+超时重传 | 可靠传输 | 保证数据最终到达 | ACK + RTO 动态调整 |
| 乱序处理 | 序列号排序 | 恢复发送顺序 | 重排序缓冲区 |
| 序列号递增 | 确保连续 | 支撑可靠机制 | 按字节递增 |
| 32位+时间戳 | 防回绕 | 区分新旧报文 | 2MSL + 时间戳 |
| 随机ISN+2MSL | 防旧报文干扰 | 隔离不同连接 | 随机初始序列号 + 等待 |
| 去重(传输层) | 丢弃重复报文 | 防止数据重复 | 序列号比对 |
| 幂等(应用层) | 业务去重 | 防止重复执行 | 唯一ID + 记录状态 |
通过电话类比、银行扣款案例和"前朝的剑斩今朝的官"比喻,我们看到了 TCP 如何在复杂网络环境中构建可靠、安全、高效的传输服务。理解这些 "是什么、为什么、怎么做",才能真正掌握网络原理的核心。

参考资料
- RFC 793 -- Transmission Control Protocol
- 《TCP/IP 详解 卷1》
- 小林 coding -- TCP 图解系列