【计算基础|网络01】网络模型与一次完整请求全景
"在浏览器地址栏敲一个网址回车,到页面画出来,中间到底发生了什么?"------这道题几乎是所有后端、运维、前端面试的开场必问题。答得碎、答得散,说明你脑子里没有一张"全景图";答得成体系,你后续排查任何网络问题(连不上、超时、被劫持、慢)都知道该从哪一层下手。
这篇不堆协议名词,先把分层模型这张地图立起来,再顺着这张地图走一遍"从 DNS 到渲染"的完整请求,最后落到 Linux 内核里一个网络包是怎么被收上来的。这是整个网络系列的地基:后面 02 篇讲 HTTP 报文、05 篇讲 HTTP/2、HTTP/3,全部建立在"请求在哪一层、用什么协议、包怎么流"这个框架上。
环境:macOS 本机 + 任意一台 Linux 服务器(内核收包流程以 Linux 为例),命令用
dig/curl/ss实测,均为系统自带或常见工具。
一、为什么要分层:先把地图立起来
结论先给:分层的本质是"关注点分离"------每一层只干自己那一段的事,对上提供服务、对下透明。 你写应用代码不用关心网线、电磁波;网卡驱动也不用关心你发的是 HTTP 还是 Redis。分层让几百个厂商、几十年的技术能拼在一张网上跑。
1.1 OSI 七层 vs TCP/IP 四层
业界两套模型:OSI 七层是"理论教材",TCP/IP 四层是"真实互联网跑的那套"。对着记,别死背:
| OSI 七层 | TCP/IP 四层 | 各层职责(一句话) | 典型协议/设备 |
|---|---|---|---|
| 应用层 | 应用层 | 给应用程序用的协议,直接面对你写的代码 | HTTP/HTTPS、DNS、SSH、FTP、Redis |
| 表示层 | 应用层 | 编解码、加密、压缩(TLS 握手、JSON 序列化算在这里) | TLS/SSL、UTF-8、Protobuf |
| 会话层 | 应用层 | 建立/管理/断开会话 | (现代基本并入应用层) |
| 传输层 | 传输层 | 端到端通信,负责可靠/不可靠、端口、流量控制 | TCP、UDP |
| 网络层 | 网络层(网际层) | 点到点路由,选路,IP 寻址 | IP、ICMP、ARP(算这层下沿)、路由器 |
| 数据链路层 | 链路层(网络接口层) | 同一局域网内帧的传输,MAC 寻址、差错检测 | 以太网、MAC、交换机 |
| 物理层 | 链路层 | 电信号/光信号/无线比特流 | 网线、光纤、集线器、网卡物理部分 |
🔴 重点:别被"七层/四层"绕晕,真正要刻进脑子的是三个分界:
- 传输层 = 端口:一台机器上跑着好几个服务(80、443、6379),靠端口区分,这是"进程到进程"。
- 网络层 = IP:把数据从这台机器送到那台机器,跨网段靠路由,这是"主机到主机"。
- 链路层 = MAC:只在同一个局域网(同一个广播域)内管用,出了局域网就靠路由器重新封装。
记住这三层,面试"一次请求在哪一层"你就不会错。
1.2 封装与分用:数据是怎么一层层套壳又一层层剥开的
结论:发送时从上往下,每经过一层就加一层"信封头"(封装);接收时从下往上,每经过一层就剥掉自己那层的头(分用/解封装)。 这就是"协议栈"三个字的来源------一层套一层的栈。

每一层加的"头"里装着这一层要用来干活的关键信息:
| 层 | 加的头叫啥 | 头里最关键的字段 | 这层靠它干嘛 |
|---|---|---|---|
| 传输层(TCP) | TCP 段头 | 源端口、目的端口、序号、确认号、标志位(SYN/ACK/FIN) | 找到对应进程、保证可靠、流控 |
| 网络层(IP) | IP 数据报头 | 源 IP、目的 IP、协议号(6=TCP, 17=UDP, 1=ICMP) | 跨网路由、知道该往上交给 TCP 还是 UDP |
| 链路层(以太网) | 帧头/帧尾 | 源 MAC、目的 MAC、类型(0x0800=IP)、FCS 校验 | 局域网内找到下一跳、校验完整性 |
⚠️ 一个容易混淆的点 :封装到 IP 头这一步,"下一跳的 MAC"不一定是最终目的主机的 MAC。IP 地址一直是最终目标不变,MAC 地址每经过一个路由器就换一次------这就是"IP 端到端、MAC 逐跳"。理解了这点,再去看 ARP、VLAN、隧道就通了。
二、键入网址到页面显示:走一遍完整流程
这是本篇的主干。把"敲回车"拆成下面 8 步,每一步我都标了它在哪一层、用什么协议,你照着这张表背,面试就不会漏。
2.1 全景时序图

2.2 逐步拆解(附协议与所在层)
| 步骤 | 发生了什么 | 所在层 | 用到的协议/工具 |
|---|---|---|---|
| ① DNS 解析 | 把 www.example.com 翻译成 IP。先查浏览器缓存 → 系统缓存 → hosts → 本地 DNS 递归 |
应用层 | DNS(UDP 53,区域传送用 TCP) |
| ② 建 TCP 连接 | 三次握手,确认双方收发能力正常 | 传输层 | TCP |
| ②.5 TLS 握手 | HTTPS 才走:协商加密套件、交换密钥、验证证书 | 表示层/应用层 | TLS/SSL(443) |
| ③ 发 HTTP 请求 | 把请求行+请求头+空行+体交给 TCP | 应用层 | HTTP/1.1、HTTP/2、HTTP/3 |
| ④ 服务器处理 | 内核收包 → 交给 Web 进程 → 查业务逻辑/数据库 → 生成响应 | 应用层 | Nginx/应用框架/数据库 |
| ⑤ 回 HTTP 响应 | 状态行+响应头+空行+体,按原路径封装回来 | 应用层 | HTTP |
| ⑥ 浏览器渲染 | 解析 HTML 构 DOM、CSSOM,布局、绘制,再对 JS/CSS/图片重复发子请求 | 应用层 | HTML/CSS/JS、HTTP |
| ⑦⑧ 断开连接 | 四次挥手(或 Keep-Alive 一段时间后再关) | 传输层 | TCP |
🔴 高频追问 :DNS 为什么用 UDP?因为 DNS 查询通常很小、要快,UDP 省掉三次握手的开销;但区域传送(传整份域名记录)或响应超过 512 字节时会改用 TCP,别一刀切说"DNS 只用 UDP"。
2.3 实测一把:看浏览器/系统到底干了啥
不写代码,用命令把上面几步"看见"。先看 DNS:
bash
# 本机直接问公共 DNS,看域名解析结果(macOS/Linux 都有 dig)
dig +short www.example.com
# 输出:
# 93.184.216.34
再看 TCP 连接和握手(-v 把过程打印出来):
bash
# -v 会先打印 DNS/连接/TLS 阶段,再打印 > < 报文
curl -sv --http1.1 https://www.example.com -o /dev/null 2>&1 | grep -E "Trying|Connected|SSL|ALPN|HTTP|using"
# 输出(关键行):
# * Trying 93.184.216.34:443...
# * Connected to www.example.com (93.184.216.34) port 443
# * ALPN: curl offers http/1.1
# * ALPN: server accepted http/1.1
# * SSL connection using TLSv1.3 / ...
# * using HTTP/1.x
# < HTTP/1.1 200 OK
你看,curl -v 的输出顺序,恰好就是上面那张表:先 Trying IP:port(这就是 DNS 的结果)→ Connected(TCP 三次握手完成)→ SSL ...(TLS 握手)→ > GET(发请求)→ < HTTP/1.1 200(收响应) 。线上排查"卡在哪一步",就是看它停在 Trying(DNS/路由问题)、Connected 之前(TCP 连不上)、还是 SSL 之后(应用层慢)。
三、TCP 三次握手与四次挥手:为什么是这个数
结论先给:三次握手是为了双方都确认"我能发、你能收,你能发、我能收";四次挥手是因为全双工下两个方向要分别关。
3.1 三次握手

🔴 为什么是三次不是两次 :两次握手,服务器收到 SYN 就建连接,但它没法确认客户端收到了自己的 SYN。更要命的是,一个历史迷路的旧 SYN 突然到了,服务器会误以为是新连接,白等资源。三次能让双方各发一次、各收一次,确认链路双向都通。
3.2 四次挥手

⚠️ 为什么挥手要四次、握手只要三次 :握手时服务器收到 SYN 可以一把把 SYN+ACK 合在一起发;挥手时,服务器收到 FIN 可能还有数据没传完,只能先回 ACK,等数据传完再单独发 FIN,所以 ACK 和 FIN 分成两步,变成四次。
⚠️ TIME_WAIT 是面试常考点 :主动关闭的一方要等 2MSL(报文最大生存时间的两倍,Linux 常约 60 秒)才彻底 CLOSED。目的有二:① 确保最后一个 ACK 能到对方(丢了对方会重发 FIN);② 让本轮连接的旧报文在网络里自然消散,避免污染下一个相同四元组的新连接。服务器上出现大量 TIME_WAIT,通常是因为服务器主动关连接(Nginx 配了短连接),不是 bug。
四、Linux 内核是怎么收一个网络包的
这是从"应用层视角"往下钻一层,也是后面讲性能优化、丢包排查的基础。结论先给:网卡收包不靠 CPU 主动去问,而是包到了网卡 → 网卡发硬中断告诉 CPU → 内核在软中断里跑协议栈逐层剥头 → 塞进 socket 缓冲区 → 应用进程 read 出来。
4.1 内核收包流程图

发送方向完全对称,只是顺序反过来:应用 write() → 进 socket 发送缓冲区 → 协议栈从上往下封装加头 → 软中断触发网卡发送 → DMA 把数据搬到网卡 → 推上网线。
🔴 几个关键认知:
- 硬中断要短 :硬中断期间 CPU 被占着、其他设备中断被阻塞,所以内核只做最紧急的"喊一嗓子",把重活丢给软中断 (下半部)慢慢干。这就是为什么高并发下看
top里si(softirq)占比高------网络包多,软中断忙。 - NAPI 是优化点 :包少时用中断来一个喊一个;包成批涌来时,硬中断通知一次后切换成 poll 模式批量收,避免"每来一个包都打断 CPU 一次"的中断风暴。万兆网卡、高 QPS 场景 NAPI 是标配。
- socket 缓冲区是个队列 :应用来不及 read,缓冲区塞满了,新到的包就只能丢------这就是"接收队列溢出导致丢包 ",
ss -lnt看到的 Recv-Q、丢包排查里的RX drops都跟它有关。
4.2 实测看一眼连接和缓冲区
bash
# 看本机监听/连接,Recv-Q/Send-Q 就是那两个内核缓冲区
ss -lnt | head
# 输出大致:
# State Recv-Q Send-Q Local Address:Port
# LISTEN 0 128 0.0.0.0:443
# LISTEN 0 128 0.0.0.0:80
Send-Q=128 是 listen 队列上限(backlog),Recv-Q 是当前已完成三次握手但还没被 accept() 取走的连接数------这个数涨满,新连接就会被拒(对应 SYN 队列溢出、listen() backlog 不够),这是线上常见的"偶发连不上"根因之一。
五、常见面试题:标准答案怎么组织
5.1 "输入 URL 后发生了什么"(标准答案结构)
别一上来就蹦名词,按这个时间线讲,逻辑就立住了:
- 解析 URL:拆分出协议(http/https)、域名、端口、路径、查询串。
- DNS 解析:查浏览器缓存 → 操作系统缓存 → hosts → 递归 DNS 服务器,拿到目标 IP。
- 建立连接:TCP 三次握手(HTTPS 紧接着 TLS 握手协商加密)。
- 发请求:封装成 HTTP 请求报文,交给协议栈逐层封装,过路由器逐跳转发。
- 服务器处理:内核软中断收包 → 协议栈剥头 → 到 socket → Web 进程处理业务 → 生成响应。
- 收响应:HTTP 响应逐层封装回来,浏览器按状态码处理(3xx 跳转、200 继续)。
- 渲染:解析 HTML 构 DOM/CSSOM,遇到 JS/CSS/图片再发子请求,布局绘制。
- 断开:四次挥手(或 Keep-Alive 复用连接)。
🔴 加分项:主动提到"中间还经过了 ARP(把下一跳 IP 解析成 MAC)、路由选路、NAT、CDN、负载均衡",并指出"HTTPS 的 TLS 握手是额外一轮延迟"。
5.2 "为什么要分层"
- 各层独立、解耦:换网线不用改应用,换 HTTP 框架不用改网卡。
- 标准化:每层定义清晰的接口和协议,不同厂商设备能互通。
- 易于排障:出问题能定位到某一层(ping 不通多半是网络层及以下,curl 能连但返回错多半是应用层)。
5.3 "一次请求涉及哪些协议"
| 阶段 | 协议 | 干的事 |
|---|---|---|
| 域名 → IP | DNS | 名称解析 |
| 找下一跳 MAC | ARP | IP→MAC(局域网内) |
| 跨网送包 | IP + ICMP | 路由、差错报告(ping 用它) |
| 可靠传输 | TCP | 三次握手、序号确认、流控、拥塞控制 |
| 加密 | TLS | 密钥协商、证书验证、加密传输 |
| 应用语义 | HTTP | 请求方法、头、状态码、体 |
总结:你真正需要记住的 8 件事
- 三层分界:传输层管端口(进程)、网络层管 IP(主机)、链路层管 MAC(局域网逐跳)。
- 封装=从上往下加头,分用=从下往上剥头;IP 端到端不变,MAC 每跳都变。
- 一次请求主线:DNS 解析 → TCP 三次握手 → (TLS 握手)→ 发 HTTP 请求 → 服务器处理 → 收 HTTP 响应 → 浏览器渲染 → 四次挥手。
- 三次握手 是为了双向确认收发能力;四次挥手是因为全双工两个方向要分别关,且被动方可能还有数据要发。
- TIME_WAIT 等 2MSL:保证最后 ACK 可达 + 让旧报文消散,服务器大量 TIME_WAIT 通常是服务器主动关连接。
- Linux 收包:网卡 DMA → 硬中断(短)→ 软中断 poll → 链路/网络/传输层逐层剥头 → 挂到 socket 缓冲区 → 应用 read。
- 硬中断要短、重活放软中断;NAPI 在高负载下把中断改成批量 poll;socket 缓冲区满了就丢包。
curl -v的输出顺序就是请求全流程 :停在Trying查 DNS/路由,停在Connected前查 TCP/防火墙,过了 SSL 才到应用层。
验证清单
- 能不看资料默写出 OSI 七层与 TCP/IP 四层的对应关系和各层协议
- 能画出封装/分用图,并说清"IP 不变、MAC 逐跳变"
- 能讲清三次握手和四次挥手的每一步,以及为什么握手三次、挥手四次
- 能说出 TIME_WAIT 的两个作用和它在哪一方出现
- 能用
dig +short把一个域名解析成 IP,并用curl -v指出每一行对应流程的哪一步 - 能描述 Linux 从网卡收到包到应用 read 出来的完整路径
- 能解释硬中断为什么要短、软中断/NAPI 解决什么问题
- 用
ss -lnt看懂 listen 队列的 Send-Q,理解 backlog 溢出的后果
参考资源
- OSI 模型(权威综述):en.wikipedia.org/wiki/OSI_mo...
- TCP 三次握手/四次挥手(RFC 9293,TCP 现行标准):www.rfc-editor.org/rfc/rfc9293
- DNS 协议(RFC 1034/1035):www.rfc-editor.org/rfc/rfc1035
- Linux NAPI 与内核收包机制(内核文档):www.kernel.org/doc/html/la...
- socket/ss 工具手册:man7.org/linux/man-p...
- MDN:HTTP 概述(为下一篇铺垫):developer.mozilla.org/zh-CN/docs/...
下一篇《网络02:HTTP 原理------报文结构、方法语义与状态码》:把"应用层那封 HTTP 信"彻底拆开------请求行/请求头/空行/请求体到底长什么样,GET/POST/PUT/PATCH 谁安全谁幂等,1xx~5xx 状态码逐个速查,并对比 HTTP/1.0 与 HTTP/1.1 的关键区别,最后用 curl -v 抓一份真实可读的报文。