【计算机网络全面教学】传输层TCP与UDP,三次握手到拥塞控制彻底搞懂Day4(2026年)

写在前面:传输层是计算机网络中最重要的一章,没有之一。你想想,面试考得最多的是什么?三次握手、四次挥手、拥塞控制------全是传输层的。我当年面试的时候,几乎每场都被问到TCP,问法还千奇百怪。这篇文章我把传输层的知识点从头到尾梳理了一遍,特别是TCP的连接管理和拥塞控制,用了大量的图解和对比表格,保证你看完之后面试不慌。建议收藏,反复看。

文章目录


一、传输层基本概念

1.1 传输层的地位

传输层位于网络层之上、应用层之下,是网络分层中最关键的一层。它为应用进程提供了端到端的通信服务。

说白了,网络层(IP)负责把数据包从主机A送到主机B,但主机B上可能同时跑着几十个程序------浏览器、微信、SSH......数据到底该交给谁?这就是传输层要解决的问题。

1.2 套接字(Socket)

套接字是传输层的通信端点,由IP地址和端口号组成:

复制代码
Socket = IP地址 + 端口号

比如 192.168.1.10:8080 就是一个套接字,唯一标识了一台主机上的一个进程。

用公寓楼类比来理解端口号:

IP地址 = 公寓楼的地址,比如"阳光公寓小区3号楼"。这栋楼里住着很多人(进程)。

端口号 = 房间号,比如808室、443室、22室。同一栋楼(同一个IP)有很多房间(端口),每个房间住不同的人(进程)。

邮递员(网络层)把包裹送到公寓楼(IP地址),楼管(传输层)根据房间号(端口号)把包裹交给具体的住户(进程)。如果没有房间号,楼管就不知道该把包裹给谁。
踩坑提醒 :新手常问"端口号和IP地址有什么区别?"答案是:IP地址定位哪台主机 ,端口号定位主机上的哪个进程。就像快递送到你家小区(IP),但还需要门牌号(端口)才能找到具体收件人。一个IP可以有65536个端口(0~65535),所以一台服务器可以同时提供很多服务。

1.3 复用与分用

  • 复用(Multiplexing):多个应用进程的数据通过同一个传输层协议发送出去(比如多个应用都用TCP发送数据)

  • 分用(Demultiplexing):传输层收到数据后,根据端口号把数据正确地交给对应的应用进程

    应用层: 浏览器(:80) 邮件客户端(:25) SSH(:22)
    | | |
    传输层: -------- TCP复用/分用 ----------
    |
    网络层: ---------- IP -----------------

问题与解答

Q1:传输层和网络层的区别是什么?

A:网络层提供主机到主机 的通信(点到点),传输层提供进程到进程的通信(端到端)。网络层用IP地址标识主机,传输层用端口号标识进程。

Q2:为什么需要传输层?应用层直接用IP不行吗?

A:不行。IP只负责把包送到目标主机,但不知道该交给哪个进程。而且IP不提供可靠传输、流量控制、拥塞控制等服务,这些都需要传输层来实现。


二、端口号详解

2.1 端口号范围

端口号是16位的无符号整数,范围是 0~65535,分为三类:

范围 名称 说明 示例
0~1023 知名端口(Well-known) 分配给常用服务,需要root/管理员权限 HTTP(80)、HTTPS(443)
1024~49151 注册端口(Registered) 给已注册的应用程序使用 MySQL(3306)、Redis(6379)
49152~65535 动态端口(Dynamic) 客户端临时使用 浏览器随机分配的端口

踩坑提醒 :在Linux上,绑定1024以下的端口需要root权限。如果你写了个Web服务想用80端口,记得用 sudo 运行,不然会报 Permission denied。这个坑我踩过不止一次。

2.2 常用端口号速查表

服务 端口号 协议 说明
HTTP 80 TCP 网页浏览
HTTPS 443 TCP 加密网页浏览
FTP 20/21 TCP 文件传输(数据/控制)
SSH 22 TCP 安全远程登录
DNS 53 TCP/UDP 域名解析
SMTP 25 TCP 发送邮件
POP3 110 TCP 接收邮件
IMAP 143 TCP 接收邮件(比POP3更强大)
Telnet 23 TCP 远程登录(不安全,已淘汰)
MySQL 3306 TCP 数据库
Redis 6379 TCP 缓存数据库
RDP 3389 TCP Windows远程桌面

记忆技巧:HTTP 80(8+0=8,最基础),HTTPS 443(比HTTP多一层安全),SSH 22(两个2,安全安全),DNS 53(我散,域名解析分散到各地)。

问题与解答

Q1:同一个端口能同时被TCP和UDP使用吗?

A:可以!端口号是TCP和UDP各自独立管理的。TCP的53端口和UDP的53端口是两个完全不同的套接字。DNS就是同时用TCP和UDP的53端口。

Q2:客户端的端口号是怎么确定的?

A:客户端通常使用临时端口号(49152~65535),由操作系统自动分配。每次新建连接都会随机选一个未使用的端口。


三、UDP协议详解

3.1 UDP特点

UDP(User Datagram Protocol,用户数据报协议)的特点用四个字概括:简单粗暴

  • 无连接:发送数据前不需要建立连接
  • 不可靠:不保证数据到达、不保证顺序、不重传
  • 面向报文:应用层交下来的报文,UDP不拆分也不合并,直接加上头部就发
  • 头部只有8字节:非常轻量

3.2 UDP头部格式

复制代码
 0       7 8     15 16    23 24    31
+--------+--------+--------+--------+
|  源端口  |  目的端口 |
+--------+--------+--------+--------+
|   长度   |  校验和   |
+--------+--------+--------+--------+
|           数据                    |
+-----------------------------------+

就这,8字节,4个字段,简单到极致。对比TCP的20字节头部(还不算选项),UDP真的是极简主义。

3.3 UDP适用场景

场景 原因
视频/音频通话 允许丢帧,但要求低延迟
直播 实时性比完整性更重要
DNS查询 请求和响应都很小,不需要建立连接的开销
游戏 需要快速传输位置信息,丢几个包无所谓
SNMP 网络管理,简单查询

踩坑提醒:很多人觉得UDP就是"不靠谱"的协议,其实UDP的"不可靠"恰恰是它的优势。对于实时性要求高的场景,TCP的重传机制反而会导致延迟越来越大。选择TCP还是UDP,要看你的业务需求。

问题与解答

Q1:UDP完全不保证可靠性吗?

A:UDP本身不提供可靠性保证。但如果应用需要,可以在UDP之上自己实现可靠传输(比如QUIC协议就是基于UDP实现的可靠传输)。UDP是"不提供,但不禁止"。

Q2:为什么DNS既用TCP又用UDP?

A:DNS查询通常用UDP(因为请求/响应很小,UDP开销低)。但当响应数据超过512字节(比如区域传输)时,会切换到TCP。另外,DNS服务器之间的区域传输也用TCP。


四、TCP协议详解

4.1 TCP特点

TCP(Transmission Control Protocol,传输控制协议)是传输层的"重量级选手":

  • 面向连接:通信前必须先建立连接
  • 可靠传输:保证数据不丢失、不重复、按序到达
  • 面向字节流:把应用层数据看作无结构的字节流
  • 全双工:双方可以同时发送和接收数据
  • 头部最小20字节:比UDP复杂得多

4.2 TCP头部格式

复制代码
 0               16              32
 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
 |        源端口号      |     目的端口号     |
 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
 |            序列号              |
 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
 |            确认号              |
 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
 |数据偏移|保留|U|A|P|R|S|F|  窗口大小  |
 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
 |    校验和    |    紧急指针     |
 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
 |           选项(可选)          |
 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+

关键字段:

字段 说明
序列号(Seq) 标识发送的数据字节流位置
确认号(Ack) 期望收到的下一个序列号
数据偏移 TCP头部长度,以4字节为单位
标志位 URG/ACK/PSH/RST/SYN/FIN,控制连接状态
窗口大小 接收方的接收缓冲区大小,用于流量控制
校验和 校验头部和数据

问题与解答

Q1:TCP为什么是面向字节流的?

A:TCP不保留应用层报文的边界。应用层发来1000字节,TCP可能拆成多个段发送,也可能合并多个小报文一起发送。接收方收到的也是连续的字节流,需要应用层自己解析报文边界。

Q2:TCP头部最大能有多少字节?

A:TCP头部最小20字节(无选项),最大60字节(选项部分最多40字节)。数据偏移字段4位,最大值15,乘以4就是60字节。


五、TCP三次握手

5.1 握手过程详解

三次握手是TCP建立连接的过程,目的是让双方确认彼此的发送和接收能力都正常。

用打电话类比来理解:

第一次握手 = 你拨号,对方电话响了(SYN)

你拿起电话,拨号给对方。这相当于客户端发送SYN报文:"喂,我想跟你通话,你能听到吗?"
第二次握手 = 对方接听,说"喂,我能听到你,你也能听到我吗?"(SYN+ACK)

对方接起电话,先回一句"我能听到你"(ACK),再问"你能听到我吗?"(SYN)。这相当于服务端回复SYN+ACK:"我收到你的请求了,我也准备好了,你那边正常吗?"
第三次握手 = 你说"我能听到你,开始聊吧"(ACK)

你听到对方的声音了,回一句"我能听到你,开始吧"。这相当于客户端发送ACK:"确认,双方都能收发,连接建立!"

复制代码
客户端                              服务端
  |                                    |
  |--- 1. SYN (seq=x) ---------------->|
  |      客户端进入 SYN_SENT 状态       |
  |                                    |
  |<-- 2. SYN+ACK (seq=y, ack=x+1) ---|
  |      服务端进入 SYN_RCVD 状态       |
  |                                    |
  |--- 3. ACK (seq=x+1, ack=y+1) ---->|
  |      双方进入 ESTABLISHED 状态     |
  |                                    |

第一步:客户端发送SYN报文

  • 标志位 SYN=1
  • 序列号 seq=x(随机生成)
  • 客户端进入 SYN_SENT 状态

第二步:服务端回复SYN+ACK报文

  • 标志位 SYN=1, ACK=1
  • 序列号 seq=y(随机生成)
  • 确认号 ack=x+1
  • 服务端进入 SYN_RCVD 状态

第三步:客户端发送ACK报文

  • 标志位 ACK=1
  • 序列号 seq=x+1
  • 确认号 ack=y+1
  • 双方进入 ESTABLISHED 状态,连接建立

5.2 序列号和确认号的变化

用具体数字来理解:

复制代码
客户端 (seq=100)                     服务端 (seq=300)
  |                                    |
  |--- SYN, seq=100 ------------------>|
  |                                    |
  |<-- SYN+ACK, seq=300, ack=101 -----|
  |                                    |
  |--- ACK, seq=101, ack=301 -------->|
  |                                    |
  |--- PSH+ACK, seq=101, data="Hello"->|
  |                                    |
  |<-- PSH+ACK, seq=301, ack=106 ------|
  |    (ack=101+5, 因为"Hello"是5字节) |
  |                                    |

踩坑提醒:注意序列号和确认号的关系------确认号 = 对方的序列号 + 已收到的数据长度。这个关系搞清楚了,三次握手和四次挥手的理解就简单多了。

5.3 为什么不能是两次握手?------旧电话留言场景

如果只有两次握手,会出什么问题?

用"旧电话留言"的场景来理解:

假设你(客户端)上周给朋友(服务端)打了一通电话,留言说"下周三我们约个饭"。但因为某种原因,这条留言在信箱里卡了很久,直到今天才送到朋友手上。

朋友收到留言后,如果只有"两次握手",他会直接认为"约饭成立",然后开始订餐厅、做准备(进入ESTABLISHED状态)。

但实际上,你早就忘了这通电话(客户端早已关闭),甚至这周已经出差了。朋友白白等了你一下午,餐厅也白订了(服务端白白维持了一个没人用的连接,浪费资源)。

三次握手怎么解决这个问题?

朋友收到旧留言后,回电给你确认:"你上周说约饭,还作数吗?"(SYN+ACK)。你接到电话一脸懵:"什么约饭?我没打过这通电话啊。"于是你不会回复确认(不发第三个ACK)。朋友就知道这条留言是旧的、失效的,不会傻傻地做准备。

技术场景还原:

客户端发送了一个SYN请求,但因为网络延迟很久才到达服务端。客户端等不及,又发了一个SYN,这次正常建立了连接并完成了数据传输。

后来那个延迟的SYN终于到了服务端,服务端以为客户端又要建立连接,于是回复SYN+ACK并进入ESTABLISHED状态。但客户端根本不会再发第三个ACK------因为它根本没发这个请求。

结果就是:服务端白白维持了一个没人用的连接,浪费资源。这就是"失效的连接请求"问题。

三次握手确保了双方都确认了彼此的收发能力,避免了这种问题。

踩坑提醒:面试官如果问"为什么不是两次握手",你不仅要回答"防止失效连接请求",还要能举出具体的场景。光背结论不够,要能讲清楚。用这个"旧电话留言"的例子,面试官一听就懂。

问题与解答

Q1:三次握手中,如果第三次ACK丢失了会怎样?

A:服务端没有收到ACK,会重传SYN+ACK。客户端收到重传的SYN+ACK后,会再次发送ACK。连接最终还是会建立成功。客户端已经进入ESTABLISHED状态,可以直接发送数据。

Q2:SYN报文中的序列号为什么要随机?

A:防止序列号预测攻击。如果序列号可预测,攻击者可以伪造TCP报文,劫持连接。随机序列号让攻击者无法猜出合法的序列号。


六、TCP四次挥手

6.1 挥手过程详解

四次挥手是TCP断开连接的过程。因为TCP是全双工的,每个方向都需要单独关闭。

用挂电话类比来理解:

第一次挥手 = A说"我说完了"(FIN)

你和朋友打电话,你说:"我要说的都说完了。"但你还没挂电话,因为朋友可能还有话要说。这相当于客户端发送FIN,表示自己没有数据要发了。
第二次挥手 = B说"好的我知道了"(ACK)

朋友说:"好的,我知道你说完了。"但他还没挂电话,因为他还有几句话要补充。这相当于服务端回复ACK,确认收到了客户端的FIN。
第三次挥手 = B说"我也说完了"(FIN)

朋友把剩下的话说完,然后说:"好了,我也说完了,可以挂了。"这相当于服务端发送FIN,表示服务端也没有数据要发了。
第四次挥手 = A说"好的再见"(ACK)

你说:"好的,再见。"然后等一会儿再挂电话(TIME_WAIT),确认朋友听到了你的"再见"。这相当于客户端回复ACK,然后等待2MSL再关闭。

复制代码
客户端                              服务端
  |                                    |
  |--- 1. FIN (seq=u) ---------------->|
  |      客户端进入 FIN_WAIT_1 状态     |
  |                                    |
  |<-- 2. ACK (ack=u+1) ---------------|
  |      服务端进入 CLOSE_WAIT 状态     |
  |      客户端进入 FIN_WAIT_2 状态     |
  |                                    |
  |     (服务端继续发送未完成的数据)       |
  |                                    |
  |<-- 3. FIN (seq=w) -----------------|
  |      服务端进入 LAST_ACK 状态       |
  |                                    |
  |--- 4. ACK (ack=w+1) -------------->|
  |      客户端进入 TIME_WAIT 状态     |
  |      等待 2MSL 后关闭              |
  |      服务端收到后直接关闭            |
  |                                    |

第一步:客户端发送FIN报文

  • 标志位 FIN=1
  • 序列号 seq=u
  • 客户端进入 FIN_WAIT_1 状态
  • 表示客户端没有数据要发送了

第二步:服务端回复ACK报文

  • 标志位 ACK=1
  • 确认号 ack=u+1
  • 服务端进入 CLOSE_WAIT 状态
  • 客户端进入 FIN_WAIT_2 状态
  • 此时客户端到服务端的方向关闭了,但服务端到客户端的方向还开着

第三步:服务端发送FIN报文

  • 服务端数据也发完了,发送FIN
  • 服务端进入 LAST_ACK 状态

第四步:客户端回复ACK报文

  • 客户端进入 TIME_WAIT 状态
  • 等待 2MSL(最长报文段寿命,通常60秒)后关闭
  • 服务端收到ACK后直接关闭

6.2 TIME_WAIT状态详解

TIME_WAIT状态持续 2MSL(约1~2分钟),这是面试的高频考点。

用等快递类比来理解TIME_WAIT:

你给朋友寄了最后一个包裹(发送最后一个ACK),然后你不能马上搬家(不能马上关闭连接),而是要等一段时间。

为什么?因为你要确认朋友确实收到了这个包裹。如果包裹在路上丢了,朋友会打电话来说"没收到"(重传FIN),你在家就能补发一个(重发ACK)。

如果你马上搬家(马上关闭连接),朋友打电话来说没收到,你已经不在原地了,他就永远等不到确认,只能干着急(服务端一直重传FIN,最终异常关闭)。

另外,等一段时间还能确保路上所有跟你有关的快递都送完了(网络中残留的报文消亡),不会影响到下一个住进来的人(新连接)。

为什么需要TIME_WAIT?

  1. 确保最后一个ACK能到达服务端。如果ACK丢失,服务端会重传FIN,客户端在TIME_WAIT期间可以重发ACK。
  2. 让网络中残留的报文消亡。等待2MSL可以确保本次连接的所有报文都从网络中消失,避免影响后续的新连接。

踩坑提醒 :在高并发短连接的服务器上,大量TIME_WAIT状态的连接会占用端口资源。可以通过调整内核参数(如 tcp_tw_reuse)来缓解,但不要随便关闭TIME_WAIT,否则可能导致连接异常。

6.3 为什么四次挥手不能是三次?

因为TCP是全双工的。客户端发FIN只是表示自己不发了,但服务端可能还有数据没发完。服务端需要先ACK确认,等数据发完后再发FIN。中间这段时间服务端可能还在传数据,所以ACK和FIN不能合并------这就是四次。

当然,如果服务端收到FIN时恰好也没有数据要发了,它可以把ACK和FIN合并成一个报文,这样就是三次挥手了。但协议设计上必须支持四次,因为大多数情况下两个方向不会同时关闭。

问题与解答

Q1:CLOSE_WAIT状态过多是什么原因?

A:CLOSE_WAIT表示对方已发FIN,但本方还没有调用 close() 关闭连接。通常是因为代码中没有正确关闭连接(比如忘记关闭数据库连接、HTTP连接没有释放等)。这是服务器端常见的Bug。

Q2:如果客户端直接断电(不发送FIN),服务端会怎样?

A:服务端不知道客户端已经断开,会一直保持ESTABLISHED状态。TCP有保活计时器(Keepalive Timer) ,默认2小时后发送探测报文,如果连续多次没有响应才关闭连接。可以通过 SO_KEEPALIVE 选项调整。


七、TCP可靠传输机制

7.1 确认应答

TCP每收到一个数据段,都会返回一个确认号(ACK),告诉发送方"我已经收到了这些数据,请继续发下一个"。

确认号的含义是:我期望收到的下一个字节的序列号

比如发送方发了 seq=1, len=1000 的数据,接收方回复 ack=1001,表示前1000字节都收到了,请从1001开始发。

7.2 超时重传

如果发送方发出数据后,在规定时间内没有收到ACK,就会重传这个数据段。

超时时间(RTO,Retransmission Timeout)的设置很讲究:

  • 太短:还没等到ACK就重传,造成不必要的重复
  • 太长:等太久才重传,效率太低

TCP使用自适应算法动态调整RTO,基于测量到的往返时间RTT来计算:

复制代码
RTO = SRTT + 4 * RTTVAR

其中 SRTT 是平滑RTT,RTTVAR 是RTT偏差。

7.3 序号与确认号

TCP对每个字节都编号,序列号是32位的,到达 2³²-1 后会回绕到0。

确认号机制保证了数据的按序到达。如果接收方收到了乱序的数据段,会缓存起来但不确认,等缺失的数据段到了再一起确认。

踩坑提醒:TCP的序列号是针对字节的,不是针对报文的!这一点很多人搞混。如果发送了两个报文,第一个 seq=1, len=500,第二个 seq=501, len=300,那么确认号应该是 ack=801(假设都收到了)。

问题与解答

Q1:如果ACK报文丢失了怎么办?

A:发送方超时后会重传数据段。接收方收到重复数据后,会再次发送ACK。所以偶尔的ACK丢失不会导致问题,只是多了一次重传。

Q2:TCP的序号回绕问题怎么解决?

A:32位序列号最大约43亿,在高速网络中可能几分钟就回绕。TCP通过**PAWS(Protection Against Wrapped Sequence Numbers)**机制,利用时间戳选项来区分新旧报文。


八、TCP流量控制

8.1 滑动窗口机制

流量控制的目的是防止发送方发太快,导致接收方缓冲区溢出 。核心机制是滑动窗口

用工厂流水线类比来理解:

想象一个工厂的生产线:

  • 发送方 = 上游工厂,不断生产产品(数据)往下游送
  • 接收方 = 下游仓库,负责接收和暂存产品
  • 缓冲区 = 仓库的容量,仓库满了就装不下了

下游仓库每隔一段时间告诉上游:"我的仓库还能装500件货"(窗口大小 = 500)。上游就根据这个信息决定发多少货,不会一股脑把所有货都倒过去。

如果下游处理得快,仓库腾出了空间,就会通知上游:"现在能装1000件了"(窗口增大)。

如果下游处理不过来,仓库快满了,就会通知上游:"只能再装100件了"(窗口缩小)。

如果仓库彻底满了,就会说:"暂停发货!"(窗口 = 0),上游就必须停下来等。

接收方在ACK报文中携带窗口大小(Window Size),告诉发送方"我还能接收多少字节的数据"。

复制代码
发送方缓冲区:
[已确认] [已发送未确认] [可以发送] [不能发送]
         <-- 滑动窗口 -->

窗口大小是动态变化的:

  • 接收方处理完数据,缓冲区腾出空间 → 窗口增大
  • 接收方来不及处理,缓冲区快满了 → 窗口缩小
  • 窗口为0时,发送方必须停止发送

8.2 零窗口探测

当接收方发送窗口为0(Win=0)时,发送方必须停止发送数据。但发送方会启动一个持续计时器 ,定期发送零窗口探测报文(1字节),询问接收方窗口是否已经打开。

如果接收方回复的窗口仍然是0,发送方继续等待。如果窗口打开了,就可以继续发送数据。

踩坑提醒:有一种叫"糊涂窗口综合征"(Silly Window Syndrome)的问题------接收方缓冲区只剩一点点空间就通知发送方,发送方就发一点点数据,导致网络效率极低。解决方案是:接收方等缓冲区有足够空间(比如一半或MSS)才更新窗口;发送方等积累足够数据(Nagle算法)再发送。

问题与解答

Q1:滑动窗口和拥塞窗口有什么区别?

A:滑动窗口(rwnd)是接收方告知的,反映接收方的处理能力;拥塞窗口(cwnd)是发送方自己估算的,反映网络的拥塞程度。发送方实际发送窗口 = min(rwnd, cwnd)。

Q2:Nagle算法是什么?

A:Nagle算法要求发送方在以下两个条件满足之一时才发送数据:①积累了一个MSS大小的数据;②收到了之前发送数据的ACK。目的是减少小包的数量,提高网络利用率。但某些场景(如游戏)需要禁用Nagle算法(设置 TCP_NODELAY)。


九、TCP拥塞控制四大算法

拥塞控制是TCP最重要的机制之一,也是面试必考内容。拥塞控制和流量控制不同:流量控制是端到端的(保护接收方),拥塞控制是全局性的(保护网络)。

用开车堵车类比来理解拥塞控制:

TCP发送数据就像在高速公路上开车。你(发送方)想知道这条路能跑多快,但又不想造成拥堵。于是你采取了一系列策略来试探路况、应对拥堵。

9.1 慢启动(Slow Start)------慢慢加速试探路况

连接刚建立时,发送方不知道网络的承载能力,所以慢慢探测

复制代码
初始:cwnd = 1 MSS
规则:每收到一个ACK,cwnd += 1 MSS
效果:cwnd 指数增长(1 → 2 → 4 → 8 → 16 ...)

虽然叫"慢启动",但其实增长速度是指数级的,一点都不慢。就像你刚上高速,从20km/h开始,每次加速都翻倍(20→40→80→160),很快就飙起来了。之所以叫"慢启动",是相对于一开始就飙到200km/h来说的。

堵车类比:你刚进入一条陌生的高速公路,不知道路况如何。于是你慢慢加速,观察车流量。如果一路畅通,你就越开越快(指数增长)。

9.2 拥塞避免(Congestion Avoidance)------保持匀速

当 cwnd 达到**慢启动阈值(ssthresh)**后,切换为拥塞避免算法:

复制代码
规则:每经过一个RTT,cwnd += 1 MSS
效果:cwnd 线性增长(每个RTT只增加1个MSS)

线性增长比指数增长慢得多,这就是"避免"的含义------小心地探测网络极限。

堵车类比:你加速到某个速度后(比如100km/h),觉得再快可能就要追尾了,于是改为每次只加一点点速度(线性增长),小心翼翼地试探这条路的极限。

9.3 快重传(Fast Rettransmit)------发现事故立即变道

当发送方连续收到 3个重复ACK 时,不等待超时,立即重传丢失的报文段。

复制代码
发送方:发送 seq=1, 2, 3, 4, 5
接收方:收到1, 2, 4, 5(3丢失了)
接收方:发送 ack=3, ack=3, ack=3(三个重复ACK)
发送方:收到3个重复ACK → 立即重传 seq=3

快重传的前提是接收方收到乱序数据时必须立即发送重复ACK,不能等。

堵车类比 :你开车时,前面3辆车都闪灯提示你"前面有事故"(3个重复ACK)。你不需要等到导航提示"前方拥堵"(超时),而是立即变道绕行(立即重传)。这样反应更快,不会傻等。

9.4 快恢复(Fast Recovery)------事故处理后恢复速度

快恢复和快重传配合使用。当触发快重传后:

复制代码
ssthresh = cwnd / 2
cwnd = ssthresh(而不是回到1!)
然后直接进入拥塞避免阶段(线性增长)

快恢复的"快"体现在:不回到慢启动(cwnd=1),而是从减半后的值开始线性增长,恢复速度更快。

堵车类比:你变道绕过了事故(快重传),但没有必要回到起点重新加速(cwnd=1)。你把速度降到原来的一半(cwnd减半),然后继续保持这个速度行驶(拥塞避免)。因为你已经知道这条路大致能跑多快了,不需要从零开始试探。

9.5 cwnd变化过程图

用文字描述一下完整的cwnd变化过程:

复制代码
cwnd
  │                              *
  │                           *     * ← 拥塞避免(线性增长)
  │                        *
  │                     *
  │                  *
  │               *
  │            *  ← 达到ssthresh,切换为线性增长
  │         *
  │      *
  │   *
  │ * ← 慢启动(指数增长)
  └────────────────────────────────── RTT

当发生超时重传时:

复制代码
ssthresh = cwnd / 2
cwnd = 1
重新慢启动

当触发快重传(3个重复ACK)时:

复制代码
ssthresh = cwnd / 2
cwnd = ssthresh
进入快恢复,然后拥塞避免
事件 ssthresh cwnd
超时重传 cwnd/2 1(重新慢启动)
快重传(3重复ACK) cwnd/2 ssthresh(快恢复)

踩坑提醒:面试中经常问"超时重传和快重传的区别"。关键区别在于cwnd的处理:超时重传回到1重新慢启动(说明网络严重拥塞),快重传只减半然后线性增长(说明网络轻度拥塞,还有报文在流通)。

问题与解答

Q1:慢启动真的是"慢"的吗?

A:不是。慢启动阶段cwnd是指数增长的(每个RTT翻倍),1→2→4→8→16...增长速度非常快。叫"慢启动"只是相对于一开始就发送大量数据而言的。

Q2:为什么快重传需要3个重复ACK而不是1个?

A:因为1个重复ACK可能只是报文乱序到达,不代表丢失。2个重复ACK也可能是巧合。连续3个重复ACK基本可以确定中间的报文确实丢失了。这是经过实践验证的阈值。


十、TCP vs UDP核心对比

用快递服务类比来理解TCP和UDP:

TCP = 顺丰快递(保价、签收、追踪)

  • 寄件前要先下单预约(三次握手)

  • 每件快递都有单号,可以追踪(序列号、确认号)

  • 如果快递丢了,顺丰会免费补发(超时重传)

  • 收件人要签字确认(ACK确认)

  • 顺丰会根据收件人的仓库容量调整发货速度(流量控制)

  • 如果某条路线堵车,顺丰会换路线或暂缓发货(拥塞控制)

  • 优点:安全可靠,东西一定不会丢

  • 缺点:贵、慢,手续多
    UDP = 普通平邮(便宜、快、不保证送达)

  • 不需要预约,直接丢进邮筒就行(无连接)

  • 没有单号,寄出去就不管了(无确认、无重传)

  • 如果信丢了,邮局不负责(不可靠)

  • 优点:便宜、快、省事

  • 缺点:信可能丢、可能迟到、可能乱序
    类比总结:你要寄一份重要合同,选顺丰(TCP);你要发一张明信片,选平邮(UDP)。

对比维度 TCP UDP
连接方式 面向连接(三次握手) 无连接
可靠性 可靠传输(确认、重传) 不可靠传输(尽最大努力)
传输方式 面向字节流 面向报文
通信模式 一对一(全双工) 一对一、一对多、多对多
头部大小 最小20字节 固定8字节
传输效率 较低(有确认和重传开销) 高(无额外开销)
流量控制 有(滑动窗口)
拥塞控制 有(慢启动、拥塞避免等)
适用场景 文件传输、网页、邮件 视频/音频、游戏、DNS

选择建议:需要可靠性选TCP,需要实时性选UDP。但别忘了,很多现代协议在UDP之上实现了可靠性(如QUIC、WebRTC),所以这个界限也在模糊。

问题与解答

Q1:HTTP用TCP还是UDP?

A:传统HTTP/1.1和HTTP/2都基于TCP。但HTTP/3基于QUIC协议,而QUIC基于UDP,所以HTTP/3实际上跑在UDP上。这是一个很好的知识点,面试时提出来能加分。

Q2:TCP和UDP可以同时使用同一个端口吗?

A:可以。TCP和UDP的端口号空间是独立的。TCP的80端口和UDP的80端口互不干扰,因为它们在操作系统中是不同的套接字。


十一、新手常见误区

学完传输层,很多新手容易在以下几个概念上踩坑。这里集中梳理一下,帮你提前避雷。

误区1:认为TCP的"可靠传输"意味着数据100%不会丢失

错误理解:"TCP是可靠传输,所以用TCP发送数据绝对不会丢。"

正确理解 :TCP的"可靠"是指它会尽力保证数据不丢失------通过确认、重传、按序交付等机制。但如果网络彻底断开(比如网线被拔了),TCP也会无能为力。TCP的可靠是"在连接正常的前提下尽力而为",不是"绝对保证"。

踩坑提醒:有些同学写代码时完全依赖TCP的可靠性,不做任何应用层的容错处理。实际上,TCP连接可能异常断开、可能长时间阻塞,应用层仍然需要处理超时、重试、断线重连等逻辑。

误区2:把"三次握手"和"四次挥手"的过程搞混

错误理解:"连接建立需要三次,断开也应该三次吧?为什么断开要四次?"

正确理解

  • 三次握手:双方只需要确认"彼此都能收发",所以第二次握手把SYN和ACK合并了,省了一次交互。
  • 四次挥手:TCP是全双工的,客户端发FIN只表示"我不发了",但服务端可能还有数据要发。所以ACK和FIN不能合并,必须分两次发送,就成了四次。

一句话记住:握手时双方"同时准备好",可以合并;挥手时双方"不同时说完",不能合并。

踩坑提醒:面试高频考点!记住这个核心区别:三次握手第二次可以合并(SYN+ACK),是因为双方同时进入"准备通信"状态;四次挥手中间不能合并,是因为服务端可能还有数据要发。

误区3:认为UDP完全不能用,TCP一定比UDP好

错误理解:"UDP不可靠,所以实际开发中都应该用TCP。"

正确理解:UDP的"不可靠"恰恰是它的优势。对于视频通话、直播、在线游戏等场景,实时性比完整性更重要。TCP的重传机制会导致延迟越来越大(比如视频卡顿后越卡越久),而UDP丢几个包只是画面稍微模糊一下,不会累积延迟。

踩坑提醒:现代很多高性能协议(如QUIC、WebRTC)都是基于UDP实现的。选择TCP还是UDP,要看业务需求:要可靠性选TCP,要实时性选UDP。没有绝对的好坏。

误区4:混淆流量控制和拥塞控制

错误理解:"流量控制和拥塞控制都是控制发送速度,作用一样吧?"

正确理解

  • 流量控制(滑动窗口) :是端到端 的控制,防止发送方发太快,导致接收方缓冲区溢出。窗口大小由接收方告知(rwnd)。
  • 拥塞控制 :是全局性 的控制,防止发送方发太快,导致网络拥塞。窗口大小由发送方根据网络状况自己估算(cwnd)。

类比:流量控制是"下游仓库装不下了,让上游慢点发";拥塞控制是"高速路上堵车了,所有车都慢点开"。

发送方实际发送窗口 = min(rwnd, cwnd),取两者中较小的一个。

踩坑提醒 :面试常问"流量控制和拥塞控制的区别"。记住:流量控制保护接收方 ,拥塞控制保护网络。两者独立工作,最终发送窗口取两者最小值。


十二、面试高频考点汇总

面试题1:详细描述TCP三次握手的过程

  1. 第一次握手:客户端发送SYN报文,SYN=1,seq=x(随机生成),客户端进入SYN_SENT状态
  2. 第二次握手:服务端收到SYN后,回复SYN+ACK报文,SYN=1, ACK=1, seq=y(随机生成),ack=x+1,服务端进入SYN_RCVD状态
  3. 第三次握手:客户端收到SYN+ACK后,发送ACK报文,ACK=1, seq=x+1, ack=y+1,双方进入ESTABLISHED状态

三次握手的本质是确认双方的发送和接收能力都正常。第一次握手服务端确认了客户端的发送能力;第二次握手客户端确认了服务端的发送和接收能力;第三次握手服务端确认了客户端的接收能力。

面试题2:详细描述TCP四次挥手的过程,以及TIME_WAIT的作用

  1. 第一次挥手:客户端发送FIN,seq=u,进入FIN_WAIT_1状态
  2. 第二次挥手:服务端回复ACK,ack=u+1,进入CLOSE_WAIT状态,客户端进入FIN_WAIT_2状态
  3. 第三次挥手:服务端发送FIN,seq=w,进入LAST_ACK状态
  4. 第四次挥手:客户端回复ACK,ack=w+1,进入TIME_WAIT状态,等待2MSL后关闭

TIME_WAIT的作用

  • 确保最后一个ACK能到达服务端。如果ACK丢失,服务端重传FIN,客户端可以重发ACK
  • 等待2MSL让网络中残留的报文消亡,避免影响后续新连接

面试题3:详细描述TCP拥塞控制的四大算法

  1. 慢启动:cwnd从1开始,每收到一个ACK就加1(指数增长),直到达到ssthresh
  2. 拥塞避免:cwnd达到ssthresh后,每个RTT只增加1个MSS(线性增长),小心探测
  3. 快重传:收到3个重复ACK时,立即重传丢失的报文段,不等待超时
  4. 快恢复:快重传后,ssthresh设为cwnd/2,cwnd设为ssthresh,直接进入拥塞避免

超时重传 时:ssthresh = cwnd/2,cwnd = 1,重新慢启动

快重传时:ssthresh = cwnd/2,cwnd = ssthresh,进入快恢复

面试题4:TCP和UDP的区别是什么?各自适用什么场景?

TCP是面向连接的可靠传输协议,提供确认重传、流量控制、拥塞控制、按序交付等服务。适用于对数据完整性要求高的场景:文件传输(FTP)、网页浏览(HTTP)、邮件(SMTP/POP3)、数据库连接。

UDP是无连接的不可靠传输协议,轻量高效,延迟低。适用于对实时性要求高的场景:视频通话、直播、在线游戏、DNS查询、SNMP。

关键区别:TCP面向字节流,UDP面向报文;TCP有拥塞控制和流量控制,UDP没有;TCP一对一通信,UDP支持多播/广播;TCP头部20字节,UDP头部8字节。

面试题5:为什么三次握手不能是两次?为什么需要TIME_WAIT?

不能是两次的原因:防止失效的连接请求建立连接。如果只有两次握手,一个延迟到达的旧SYN请求会导致服务端建立无效连接,浪费资源。三次握手确保了客户端最后发送ACK确认,服务端只有在收到ACK后才会建立连接。

需要TIME_WAIT的原因

  1. 确保最后一个ACK能到达服务端。如果ACK丢失,服务端会重传FIN,TIME_WAIT状态下的客户端可以重发ACK
  2. 让本次连接的所有报文在网络中消亡(2MSL时间),避免残留报文影响新连接

十三、模拟测试题

选择题/填空题

1. TCP三次握手中,第二次握手服务端发送的报文标志位是( )。

2. 以下哪个协议使用UDP作为传输层协议?

  • A. HTTP
  • B. FTP
  • C. DNS
  • D. SMTP

3. TCP拥塞控制中,当检测到超时时,cwnd被设为( ),ssthresh被设为( );当收到3个重复ACK时,cwnd被设为( ),进入快恢复阶段。

4. TCP四次挥手中,主动关闭方发送第一个FIN后进入( )状态,收到对方的ACK后进入( )状态,发送最后一个ACK后进入( )状态。

5. UDP头部大小为( )字节,TCP头部最小为( )字节。端口号的取值范围是( )到( )。


参考答案

1. SYN=1, ACK=1(即SYN+ACK报文)。

2. C。DNS默认使用UDP协议(端口53),在响应数据过大时才切换到TCP。HTTP、FTP、SMTP都基于TCP。

3. 1;cwnd/2;ssthresh(即cwnd/2)。超时重传时cwnd归1重新慢启动,快重传时cwnd减半后进入快恢复。

4. FIN_WAIT_1;FIN_WAIT_2;TIME_WAIT。

5. 8;20;0;65535。


互动话题

你在面试中遇到过哪些关于TCP的"刁钻"问题?或者在实际开发中有没有因为TCP的某些特性踩过坑(比如TIME_WAIT过多、粘包问题等)?欢迎在评论区分享,大家一起讨论!


参考资料

  1. RFC 793 - Transmission Control Protocol (TCP)
  2. TCP/IP详解 卷1:协议 - W. Richard Stevens
  3. Wireshark官方文档 - TCP分析

本文为计算机网络系列教学文章第4篇,下一篇我们将进入应用层,详细讲解HTTP、DNS、FTP等应用层协议。敬请期待!

相关推荐
weixin_4624462312 小时前
手把手教你用 Bash 脚本自动更新 /etc/hosts —— 自动绑定网卡 IP 与节点名
开发语言·tcp/ip·bash
上海云盾-小余16 小时前
源站隐藏实战:规避裸 IP 被直接攻击的完整方案
数据库·网络协议·tcp/ip
TechWayfarer18 小时前
云服务器地域怎么选:用离线IP数据库识别用户来源并优化部署
服务器·数据库·python·tcp/ip·数据分析
爱吃苹果的梨叔19 小时前
2026年KVM over IP采购指南:BIOS级接管、并发和审计怎么验收
ide·python·tcp/ip·github
IpdataCloud19 小时前
跨境支付如何识别高风险IP?用IP风险画像服务选型与集成指南
服务器·网络·数据库·tcp/ip·安全
ALINX技术博客20 小时前
【黑金云课堂】FPGA技术教程Vitis开发:TCP以太网通信
网络协议·tcp/ip·fpga开发
JZZC221 小时前
第1章 计算机网络概论-历史
计算机网络·历史
kidding72321 小时前
高效备忘清单工具类小程序
前端·计算机网络·微信小程序·小程序
W.W.H.1 天前
Ping 与 TCP:网络连通性探测的两种维度
网络·网络协议·tcp/ip