📦

小包的奇幻漂流

一个数据包从「敲下回车」到「页面渲染」的完整旅程
覆盖面试高频网络知识点,故事读完 = 知识闭环

序章0%
序章

用户敲下回车,小包诞生了

周五晚上,你在浏览器地址栏敲下 https://taobao.com 然后按了回车。这一瞬间,一个「请求包」诞生了——我们叫它小包

小包很兴奋,但它啥也不知道:taobao.com 在哪?怎么过去?路上安不安全?

于是,一场横跨整个网络协议栈的冒险开始了。

第一步:问路 —— DNS 解析

小包先去找 DNS(域名系统)。这就像出门前先查地图——把 "taobao.com" 翻译成一个 IP 地址,比如 140.205.220.96

查询顺序:浏览器缓存 → OS缓存 → 本地hosts → 本地DNS服务器 → 根DNS → 顶级域DNS → 权威DNS,一层层问下去,直到找到答案。

🎯 面试考点

「从输入URL到页面展示」这道题的第一步就是DNS解析。很多人答成直接建TCP连接,直接丢分。记住:先问路,再上路

第一章

三次握手 —— 小包的接头暗号

小包拿到了IP地址,但它不能直接闯进去。TCP是个讲礼貌的协议,必须先「握手」确认双方都在、都准备好了。

想象你和朋友打电话:

客户端(你) 服务端(淘宝) SYN, seq=x "喂,听得到吗?" SYN+ACK, seq=y, ack=x+1 "听到了,你听到我吗?" ACK, 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,ack=y+1。双方进入 ESTABLISHED,通道建好。

🎯 面试考点:为什么是三次,不是两次?

核心答案:三次握手的本质是确认双方的收发能力都正常

两次只能证明「客户端能发,服务端能收」,但服务端不知道自己发出去的包客户端能不能收到。第三次握手就是客户端告诉服务端:「你的发送能力OK,我收到了」。

另一个关键原因:防止历史重复连接。假如客户端之前发过一个SYN但网络延迟了,后来又发了新的SYN。两次握手的话,服务端收到旧SYN就直接建连接,浪费资源。三次握手下,客户端收到旧SYN的ACK后会发现序列号对不上,发RST拒绝,避免脏连接。

第二章

TLS握手 —— 小包穿上防弹衣

TCP通道建好了,但你输入的是 https://,这意味着通信必须加密。小包需要先和服务端协商一套加密方案——这就是 TLS 握手

打个比方:TCP握手是你和快递员确认了彼此身份,TLS握手是你们再商量好用什么密码来锁快递箱,防止中途被别人打开。

客户端 服务端 ClientHello 支持的加密套件、随机数A ServerHello + 证书 选定的加密套件、随机数B、证书 [验证证书 → 生成预主密钥] 用服务端公钥加密预主密钥 [双方用 随机数A + B + 预主密钥 → 会话密钥] Finished Finished 对称加密通信开始
🎯 面试考点:HTTP vs HTTPS
维度HTTPHTTPS
端口80443
安全性明文传输,裸奔TLS加密,防窃听/篡改/冒充
性能无额外开销多一次TLS握手(TLS 1.3 只需1-RTT)
证书不需要需要CA颁发的数字证书

关键理解:TLS握手用非对称加密(RSA/ECDHE)交换密钥,之后通信用对称加密(AES)。因为非对称加密慢、对称加密快,两者结合是最优解。

第三章

发出请求 —— 小包带着任务出发

加密通道就绪。小包终于可以携带真正的 HTTP 请求出发了。浏览器构造了一个 GET / HTTP/1.1 请求,附上 Host、User-Agent、Cookie 等头部信息。

🎯 面试考点:GET vs POST
维度GETPOST
语义获取资源(幂等)提交数据(非幂等)
参数位置URL query string请求体 body
长度限制浏览器/服务器限制URL长度(通常2KB~8KB)无固有限制
缓存可被缓存、收藏、记录历史默认不缓存
安全性参数暴露在URL,不适合敏感数据参数在body,相对不暴露(但不加密仍不安全)

高频追问:GET和POST在TCP层面有区别吗?本质上没有,都是TCP连接上发的数据。所谓"GET产生一个TCP包,POST产生两个"是某些浏览器实现(先发header再发body)的行为,不是协议规定

🎯 面试考点:HTTP/1.0 vs 1.1 vs 2.0
版本核心改进
HTTP/1.0每次请求都要新建TCP连接(短连接),请求完就断
HTTP/1.1持久连接(keep-alive,默认开启)复用TCP连接
管道化(pipelining)允许连续发请求不等响应
③ 但响应必须按序返回队头阻塞问题
HTTP/2.0多路复用(一个TCP连接上并行多个stream,彻底解决应用层队头阻塞)
头部压缩(HPACK)
服务端推送
二进制分帧(不再是纯文本)

注意:HTTP/2 解决了应用层队头阻塞,但底层 TCP 的队头阻塞仍在(一个丢包会阻塞整个连接的所有stream)。这也是 HTTP/3 改用 QUIC(基于UDP)的原因。

第四章

路上的交通管制 —— 滑动窗口与拥塞控制

小包上路了,但网络不是只有它一个。就像高速公路,车太多会堵。TCP用两个机制保证不「撑爆网络」也不「饿死自己」。

一、滑动窗口(Flow Control)—— 接收方的容量

接收方通过 ACK 告诉发送方:「我的缓冲区还剩多少空间」,这个值叫 rwnd(接收窗口)。发送方不能发超过这个量的未确认数据。

类比:你朋友说「我桌上只能放5个快递,别一次寄太多」。你送了3个,他拆了2个腾出空间说「现在能放4个了」,窗口就「滑动」了。

发送方视角: 已确认 已发送未确认 (在途) 可发送 (窗口内) 不可发送 发送窗口 = min(rwnd, cwnd)

二、拥塞控制(Congestion Control)—— 网络的承载力

滑动窗口管的是「接收方吃不吃得下」,拥塞控制管的是「路上堵不堵」。发送方维护一个 cwnd(拥塞窗口),实际发送窗口 = min(rwnd, cwnd)。

① 慢启动(Slow Start):cwnd 从 1 开始,每收到一个 ACK,cwnd 翻倍(指数增长)。虽然叫"慢",其实增长很快。

② 拥塞避免(Congestion Avoidance):当 cwnd 达到 ssthresh(慢启动阈值),改为每个 RTT 只 +1(线性增长),小心翼翼地探测上限。

③ 快重传(Fast Retransmit):收到 3 个重复 ACK(说明某个包丢了但后面的包还在到),立即重传丢失的包,不等超时。

④ 快恢复(Fast Recovery):快重传后,ssthresh = cwnd/2,cwnd = ssthresh(而不是从1重新慢启动),然后继续拥塞避免。

cwnd 时间 ssthresh 慢启动(指数) 拥塞避免(线性) 丢包! → ssthresh = cwnd/2, cwnd 降低
🎯 面试考点

面试官问「TCP怎么保证可靠传输」,很多人只答重传。完整答案要包含四个机制:① 序列号+确认号 ② 超时重传+快重传 ③ 滑动窗口(流量控制) ④ 拥塞控制。这四个缺一不可。

第五章

服务端回信 —— 小包拿到了回执

淘宝服务器处理完请求后,返回一个 HTTP 响应。响应里最重要的就是状态码——它决定了小包的命运。

类别含义高频考点
2xx成功200 OK · 201 Created · 204 No Content
3xx重定向301 永久重定向(可缓存) · 302 临时重定向 · 304 Not Modified(命中缓存)
4xx客户端错误400 Bad Request · 401 未认证 · 403 Forbidden · 404 Not Found · 405 Method Not Allowed
5xx服务端错误500 Internal Error · 502 Bad Gateway · 503 Service Unavailable · 504 Gateway Timeout
🎯 面试考点

301 vs 302:301是永久,浏览器会缓存新地址下次直接访问;302是临时,下次还访问原地址。SEO角度301会转移权重。

401 vs 403:401是「你没登录/认证失败」(身份不明),403是「我知道你是谁,但你没权限」(身份明确但越权)。

502 vs 504:都和网关/代理有关。502是网关从上游收到了无效响应;504是网关等上游超时了没收到响应。

第六章

记住你是谁 —— Cookie 与 Session

HTTP 是无状态的——服务端不记得「你」。但你登录了淘宝,刷新页面还是登录状态,谁帮你记住的?

Cookie:服务端通过 Set-Cookie 响应头给浏览器塞一张「身份小纸条」,之后浏览器每次请求都自动带上这张纸条。数据存在客户端

Session:服务端自己开一个「小本本」记你的登录状态,只给你一个 SessionID(通常放在Cookie里传递)。数据存在服务端

浏览器 服务端 第一次登录: POST /login (账号密码) Set-Cookie: JSESSIONID=abc123 (内存存了 abc123→用户信息) 后续请求: GET /order Cookie: JSESSIONID=abc123 (服务端用abc123查到你是谁,返回你的订单)
🎯 面试考点:Cookie vs Session
维度CookieSession
存储位置客户端(浏览器)服务端(内存/Redis)
安全性用户可见可篡改,较低用户拿不到数据,较高
大小限制4KB左右取决于服务端存储
跨域受同源策略限制不涉及(服务端存储)
分布式问题无(客户端自带)有(多节点要共享Session,通常用Redis)

进阶追问:现在更主流的做法是用 JWT(JSON Web Token) 替代 Session。JWT 把用户信息编码成一个签名Token放在客户端,服务端无状态验签即可,天然解决分布式Session问题。trade-off 是 Token 无法主动失效(除非引入黑名单机制)。你做的秒杀系统如果用的 Dubbo 微服务架构,面试官很可能从这里追问到你的认证方案。

第七章

四次挥手 —— 小包的告别仪式

页面数据传完了。TCP要断开连接,但不能直接挂电话——万一对方还有话没说完呢?所以需要四次挥手

客户端 (主动关闭方) 服务端 (被动关闭方) FIN, seq=u "我说完了" [FIN_WAIT_1] ACK, ack=u+1 "收到,但我可能还没说完" [FIN_WAIT_2] [CLOSE_WAIT] (服务端继续发送剩余数据...) FIN, seq=w "好,我也说完了" [LAST_ACK] ACK, ack=w+1 "好的,再见" [TIME_WAIT → 等待2MSL] [CLOSED] 连接关闭

为什么是四次不是三次?因为TCP是全双工的——两个方向的通道独立关闭。收到对方的FIN只表示对方不发了,但自己可能还有数据要发,所以 ACK 和 FIN 不能合并成一步(除非恰好同时没数据了,实际中偶尔也会三次)。

🎯 面试考点:TIME_WAIT

TIME_WAIT 是什么:主动关闭方发完最后一个ACK后,不直接关,而是等待 2MSL(MSL = 报文最大存活时间,通常60秒)。

为什么要等

确保最后的ACK到达:如果这个ACK丢了,服务端会重发FIN,客户端还在TIME_WAIT就能重发ACK。如果直接关了,服务端重发的FIN会收到RST,导致异常。

让旧连接的包在网络中消散:防止新连接收到上一次连接的残留数据包。

生产问题:高并发短连接场景下,大量 TIME_WAIT 会耗尽端口。你的秒杀项目用 Dubbo 长连接就不太有这问题,但如果面试官追问,解决方案包括:开启 tcp_tw_reuse、使用长连接池、让服务端主动关闭(TIME_WAIT转移到服务端)。

第八章

番外篇 —— 小包的冒失兄弟 UDP

小包走的是 TCP 这条「保镖护送路线」,它还有个兄弟叫 UDP包,性格完全相反——不握手、不确认、不管丢包,拿到数据就直接扔出去。

这不是缺点,而是设计选择

维度TCPUDP
连接面向连接(三次握手)无连接,直接发
可靠性可靠(重传、确认、排序)不可靠(丢了就丢了)
有序性保证有序不保证
传输方式字节流数据报(保留消息边界)
头部开销20字节8字节
速度较慢(可靠有代价)快(轻量无负担)
典型场景HTTP、文件传输、邮件DNS、视频直播、游戏、QUIC
🎯 面试考点

「TCP一定比UDP好吗?」不是。视频通话中丢一帧画面,用户几乎感知不到,但如果等TCP重传那一帧,整个画面就卡住了。所以实时性要求高、能容忍少量丢包的场景用UDP。

你的 RPC 框架用的 Netty 默认走 TCP,面试官可能追问「Dubbo 为什么用TCP不用UDP」——因为 RPC 调用要求每个请求都必须有完整响应,可靠性是刚需。

终章

小包回家 —— 从字节到像素

小包的旅程快结束了。让我们把整个「从输入URL到页面展示」的闭环合起来:

① DNS解析 → taobao.com → 140.205.220.96 ② TCP三次握手 → 建立可靠连接 ③ TLS握手 → 协商加密(如果是HTTPS) ④ 发送HTTP请求 → GET / HTTP/2 ⑤ 服务端处理 → 后端逻辑、查DB、组装响应 ⑥ 返回HTTP响应 → 状态码 + 响应头 + HTML ⑦ 浏览器解析 → HTML→DOM树 CSS→CSSOM树 ⑧ 渲染 → DOM+CSSOM→Render Tree→布局→绘制→合成 ⑨ 执行JS → 可能修改DOM,触发重排/重绘 ⑩ TCP四次挥手 → 连接释放(HTTP/1.0)或保持复用(HTTP/1.1+)
🎯 完整答题模板

面试官问这道题时,按「网络层 → 传输层 → 应用层 → 浏览器渲染」四个层次回答,每层点到关键词即可。不需要每个细节都展开,但每层都要提到,展示你的全栈视野。如果面试官对某层感兴趣,他会追问,那时候再深入。

作为Java后端,重点说 ②③④⑤⑥ 这几步,浏览器渲染部分简单带过就行。

附录

面试前速查卡

把所有知识点压缩成一句话记忆:

知识点一句话记忆
三次握手确认双方收发能力 + 防历史连接
四次挥手全双工所以两个方向分别关闭
TIME_WAIT等2MSL:保最后ACK到达 + 旧包消散
TCP vs UDP可靠有序 vs 快速轻量,按场景选
拥塞控制慢启动(指数) → 拥塞避免(线性) → 快重传(3次dupACK) → 快恢复
滑动窗口接收方告诉发送方"我还能吃多少"
HTTP状态码2成功 3跳转 4你的错 5我的错
HTTPSHTTP + TLS = 非对称换密钥 + 对称加密通信
HTTP/2多路复用 + 头部压缩 + 二进制分帧 + 服务端推送
GET vs POST语义不同(获取 vs 提交),本质都是TCP数据
Cookie vs Session客户端纸条 vs 服务端小本本,Session常靠Cookie传ID
URL→页面DNS → TCP → TLS → HTTP → 服务端 → 响应 → 渲染 → 挥手
思考题

面试深水区 —— 灵魂拷问五连

以下问题在大厂面试中高频出现,答案需要你把前面的知识融会贯通。先自己想,再展开看参考答案。

Q1:三次握手中,如果第三次 ACK 丢了,会发生什么?

服务端视角:服务端停留在 SYN_RCVD 状态,没收到 ACK,会认为自己的 SYN+ACK 丢了,于是重传 SYN+ACK(Linux 默认重传 5 次,间隔指数退避)。重传次数耗尽后,服务端放弃该半连接,回收资源。

客户端视角:客户端已经进入 ESTABLISHED。如果客户端此时发送数据,数据包里自然携带 ACK 信息,服务端收到后也会进入 ESTABLISHED——所以连接最终大概率还是能建立。

追问陷阱:面试官常接着问「那 SYN Flood 攻击是什么原理?」——攻击者伪造大量 SYN 但不回 ACK,服务端半连接队列被塞满,正常用户无法建连。防御手段:SYN Cookie(不在半连接队列保存状态,把状态编码进 SYN+ACK 的序列号里)。

Q2:服务端出现大量 CLOSE_WAIT 是什么原因?和大量 TIME_WAIT 有什么本质区别?

CLOSE_WAIT 堆积:被动关闭方收到对端 FIN 后进入 CLOSE_WAIT,等待自己调用 close()。如果大量堆积,几乎一定是代码 bug——程序没有正确关闭 socket(连接泄漏)。这是最常见的线上故障根因之一

TIME_WAIT 堆积:主动关闭方的正常行为,不是 bug。在高并发短连接场景(如 HTTP/1.0)下,大量 TIME_WAIT 会耗尽可用端口。

维度CLOSE_WAIT 堆积TIME_WAIT 堆积
本质代码 bug(未 close)协议正常行为
出现在被动关闭方主动关闭方
解决方式修代码,确保关闭连接tcp_tw_reuse / 长连接池 / 调大端口范围

面试加分点:能说出「CLOSE_WAIT 查代码,TIME_WAIT 调参数」这个结论,说明你有线上排障经验。

Q3:TCP 的 keepalive 和 HTTP 的 Keep-Alive 是同一个东西吗?

完全不同,但名字相似经常被混淆。

维度TCP keepaliveHTTP Keep-Alive
层级传输层(内核实现)应用层(HTTP 头部)
作用心跳探测,检测死连接复用 TCP 连接,减少握手开销
默认行为Linux 默认 2 小时才发第一个探测包HTTP/1.1 默认开启
解决的问题对端主机崩溃/网线断了,我怎么知道?每个请求都重新三次握手太慢

追问:「那为什么应用层(Dubbo、gRPC)还要自己做心跳,不直接用 TCP keepalive?」——因为 2 小时太长,中间件需要秒级感知节点下线。而且 TCP keepalive 只能检测连接是否存活,无法检测应用层是否健康(比如进程假死、线程池满)。

Q4:一个 TCP 连接上能同时发多个 HTTP 请求吗?HTTP/1.1、HTTP/2、HTTP/3 分别是什么情况?

版本能否并行队头阻塞
HTTP/1.1pipelining 允许连续发,但响应必须按序返回:前一个响应慢,后面全堵
HTTP/2多路复用,同一连接上并行多个 stream应用层无,但 TCP 层有:一个丢包阻塞所有 stream
HTTP/3基于 QUIC(UDP),每个 stream 独立彻底解决:丢包只影响对应 stream

关键理解:HTTP/2 的队头阻塞发生在传输层——TCP 把所有 stream 的数据看作一个字节流,一个包丢了整个流都要等重传。这就是 HTTP/3 必须换底层协议的根本原因。

面试加分点:如果你能画出「HTTP/2 一个 TCP 连接 → 多个 stream → TCP 层只看到一个字节流 → 丢包阻塞所有 stream」这条因果链,面试官会认为你真正理解了协议演进的驱动力。

Q5:TCP「保证可靠传输」这句话准确吗?TCP 的可靠性边界到底在哪?

不完全准确。TCP 保证的是:数据从发送方内核缓冲区可靠地到达接收方内核缓冲区,顺序正确、不丢不重。

但它不保证

应用层已读取:ACK 代表内核收到了数据,不代表应用层 read() 了。如果接收方进程在读取前崩溃,数据就丢了。

业务已处理:你的 HTTP 响应返回 200,只代表数据到了对端内核。对端应用可能还没解析完就挂了。

网络永远可达:如果网络中断时间超过 TCP 重传上限,连接会被 reset,数据照样丢。

面试高光时刻:能说出「TCP 的可靠性是传输层承诺,不是端到端承诺」这句话,说明你对分层模型有深刻理解。这也是为什么关键业务(如支付)需要应用层确认机制(如消息队列的 ACK、幂等接口 + 对账)来兜底。

📦 小包已安全抵达 ✅

从DNS出发,经过TCP握手、TLS加密、HTTP传输、拥塞控制,到达服务端拿到响应,最终回到浏览器渲染成页面,再通过四次挥手优雅告别。

整个网络知识闭环,你现在都走过一遍了。