📖 Day 3 / 15 · 《计算机网络:自顶向下方法》阅读笔记

每天 30 分钟,15 天读懂计算机网络。今天我们正式进入协议栈的顶层——应用层,并深入拆解你最熟悉的 HTTP 协议。

一、应用层:协议栈的”顶楼”#

还记得 Day 1 我们说的分层模型吗?协议栈从上到下分为五层:应用层 → 传输层 → 网络层 → 链路层 → 物理层

今天我们从最顶层——应用层(Application Layer)——开始。

为什么从顶层开始?因为这是离你最近的一层。你每天刷的网页、发的微信、看的视频,全都在这一层运行。《自顶向下方法》这本书的精髓就在这里:从你最熟悉的地方开始,一层层往下拆

🏢 生活类比:写字楼

把协议栈想象成一栋五层的写字楼:

  • 5楼(应用层):前台大厅,客户直接打交道的地方
  • 4楼(传输层):收发室,负责确保包裹准确送达
  • 3楼(网络层):物流调度中心,规划运输路线
  • 2楼(链路层):货车调度站,负责相邻站点间的运输
  • 1楼(物理层):公路本身, trucks 和柏油路

你作为普通用户,永远只在 5 楼活动。但 5 楼能正常工作,离不开楼下所有人的配合。


二、网络应用程序的架构#

写一个网络应用,首先得想清楚:架构怎么设计?

常见的有两种思路:

2.1 客户端-服务端架构(C/S)#

这是最经典、最常见的架构:

  • 服务端(Server):一直开着,等待请求,提供服务
  • 客户端(Client):主动发起请求,接收服务

你的博客就是这个架构:

1
2
3
浏览器(客户端)──HTTP 请求──▶ nginx(服务端)

返回博客页面

C/S 架构的特点:

特性 说明
客户端之间不直接通信 你的浏览器和别人的浏览器不会互相聊天
服务端需要固定地址 域名 + 端口,比如 bc970321.cn:80
服务端需要 always-on 7×24 小时在线,否则用户就访问不了

💡 你的实际情况

你的 Mac mini 上跑着 nginx 监听 8080 端口,通过 frp 把 80 端口的流量转发到 8080。所以你的服务端架构其实是:

1
用户 → bc970321.cn(80) → frps 转发 → Mac mini nginx(8080)

frp 在这里扮演了一个”中转站”的角色,本质上是把你的 Mac mini 从内网”暴露”到了公网。这仍然是 C/S 架构,只是中间多了一层代理。

2.2 P2P 架构(Peer-to-Peer)#

P2P 中没有固定的”服务端”,每个节点既是客户端也是服务端:

  • 节点之间直接通信
  • 不需要 always-on 的中心服务器
  • 可扩展性极强(节点越多越强大)

🎲 生活类比

  • C/S 就像去银行办业务——你是客户,银行是服务端,你排队、提需求、银行处理
  • P2P 就像朋友之间互相借东西——没有固定的”借出方”,今天你借我的,明天我借你的

常见的 P2P 应用:BitTorrent(下载)、区块链、Skype(早期版本)

P2P 的内容我们 Day 4 会更详细讨论,今天先聚焦 C/S 架构下的重头戏——Web 和 HTTP


三、进程通信与 Socket#

在讲 HTTP 之前,先搞清楚一个基础问题:应用层到底是谁在通信?

答案:进程(Process)

你的浏览器是一个进程,nginx 也是一个进程。网络通信的本质,就是不同主机上的进程之间交换数据

3.1 进程怎么通信?——通过 Socket#

应用层不直接操作网线和路由器,它通过一个叫做 Socket(套接字) 的接口和下层打交道。

🔌 生活类比:墙上的插座

Socket 就像墙上的电源插座。

  • 你的电器(应用进程)只需要插上插座就能用电
  • 你不需要关心电是从哪个发电厂发出来的、经过了哪些变电站
  • 插座后面的事情(传输层、网络层、物理层)全部由电力公司(操作系统)搞定

Socket 就是应用程序和网络之间的”插座”。开发者只需要知道怎么用 Socket 发送和接收数据,不需要关心数据在网络中具体怎么传输。

3.2 进程的地址#

要让两个进程通过网络通信,你需要知道两件事:

  1. 对方主机的 IP 地址:找到是哪台机器(就像小区地址)
  2. 对方的端口号:找到是机器上的哪个进程(就像门牌号)

🏠 类比你的博客

  • IP 地址 = Mac mini 在网络中的位置
  • 端口号 8080 = nginx 进程在 Mac mini 上的”门牌号”
  • 当浏览器访问 bc970321.cn 时,DNS 会解析出公网 IP,然后 frp 把请求转发到 Mac mini 的 8080 端口,nginx 进程就收到了请求

四、HTTP 协议到底是什么?#

终于到主角了——HTTP(HyperText Transfer Protocol,超文本传输协议)

HTTP 是 Web 的基石。你的浏览器每次打开网页、每次刷新、每次提交表单,底层都在用 HTTP。

4.1 HTTP 的定义#

HTTP 是一个客户端-服务端协议

  • 客户端(浏览器)发送 HTTP 请求(Request)
  • 服务端(nginx 等)返回 HTTP 响应(Response)

就这么简单。HTTP 定义了请求和响应的格式,双方都按这个格式说话,就能互相理解。

📬 生活类比:写信

HTTP 就像一套写信的规范:

  • 请求:你写给对方的信(”我要看首页的内容”)
  • 响应:对方回你的信(”给你首页的 HTML”)
  • HTTP 方法:信的类型(GET = 只是看看,POST = 提交新内容)
  • 状态码:回信封上的标记(200 = 正常收到,404 = 找不到,500 = 我出问题了)

整个 Web 就是无数封这样的”信”在网络中飞来飞去。

4.2 HTTP 是无状态的#

HTTP 有一个非常重要的特性:无状态(Stateless)

服务端不会记住”你之前来过”。每一次请求都是独立的,服务端对每个请求的处理完全一样,不管你是不是上一秒刚访问过。

🏪 生活类比:健忘的便利店店员

想象一个记忆力只有 3 秒的便利店店员。你每次买东西,他都会问:”你好,请问需要什么?”——即使你 1 分钟前刚买过同样的东西。

HTTP 就是这个健忘的店员。你每次请求,它都当成第一次见面。

那网购购物车怎么记住我买了什么?——用 CookieSession。相当于你每次去都带一张”会员卡”,上面记录了你的信息。服务端看卡识别你,而不是靠记忆。

4.3 HTTP 与 TCP 的关系#

HTTP 使用 TCP 作为传输层协议(而不是 UDP):

1
2
应用层:    HTTP
传输层: TCP(提供可靠传输)

TCP 为 HTTP 提供了可靠的数据传输服务。HTTP 不需要担心数据丢失、乱序、重复——这些 TCP 全帮你搞定了。

🚚 类比

HTTP 是寄件人,TCP 是快递公司。HTTP 只管写好信的内容,交给 TCP 就行。TCP 保证信件完整、按顺序送到对方手里。如果中途丢了,TCP 会自动重发。

HTTP 建立 TCP 连接需要经过”三次握手”(Day 5 会详细讲)。这意味着每个 HTTP 请求之前,都需要先花时间建立连接。


五、HTTP 请求与响应:逐行拆解#

这是今天的重头戏。让我们直接看真实的 HTTP 报文。

5.1 HTTP 请求报文#

当你在浏览器输入 bc970321.cn 并回车,浏览器发出的 HTTP 请求大概长这样:

1
2
3
4
5
6
GET / HTTP/1.1
Host: bc970321.cn
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7)
Accept: text/html,application/xhtml+xml
Accept-Language: zh-CN,zh;q=0.9
Connection: keep-alive

逐行解读:

含义
GET / HTTP/1.1 请求行:用 GET 方法,请求根路径 /,使用 HTTP/1.1 版本
Host: bc970321.cn 目标主机名(虚拟主机靠这个区分)
User-Agent: ... 客户端信息(浏览器类型、操作系统)
Accept: ... 客户端能接受的内容类型
Accept-Language: ... 偏好语言
Connection: keep-alive 保持连接,别断

💡 请求行的格式

1
方法 URL HTTP版本

三部分用空格隔开。常见的 HTTP 方法:

方法 含义 类比
GET 获取资源 “我要看看这个网页”
POST 提交数据 “我要提交这个表单”
PUT 更新资源 “把这个文件改成这样”
DELETE 删除资源 “把这篇文章删掉”
HEAD 只获取头部信息 “我只想看看简介”

5.2 HTTP 响应报文#

nginx 收到请求后,返回的响应大概长这样:

1
2
3
4
5
6
7
8
9
10
11
HTTP/1.1 200 OK
Server: nginx/1.25.0
Content-Type: text/html; charset=UTF-8
Content-Length: 15234
Connection: keep-alive

<!DOCTYPE html>
<html>
<head><title>我的博客</title></head>
<body>...</body>
</html>

结构分三部分:

  1. 状态行HTTP/1.1 200 OK(版本 + 状态码 + 状态短语)
  2. 头部行:Server、Content-Type、Content-Length 等
  3. 实体体(Body):实际的 HTML 内容

5.3 HTTP 状态码#

状态码是服务端告诉客户端”事情办得怎么样了”的方式:

状态码 类别 含义 常见例子
1xx 信息性 请求已收到,继续处理 101 Switching Protocols
2xx 成功 请求成功处理 200 OK
3xx 重定向 需要进一步操作 301 永久重定向, 302 临时重定向, 304 Not Modified
4xx 客户端错误 你搞错了 404 Not Found, 403 Forbidden, 400 Bad Request
5xx 服务端错误 我挂了 500 Internal Server Error, 502 Bad Gateway, 503 Service Unavailable

🔍 实战分析:你博客上的状态码

  • 用户正常访问博客 → 200 OK
  • 访问不存在的文章 → 404 Not Found ⚠️
  • frp 隧道断了,nginx 没响应 → 502 Bad Gateway 💥
  • 你在 nginx 配置了 HTTPS 重定向 → 301 永久重定向 🔀
  • 浏览器缓存还有效,不用重新下载 → 304 Not Modified

下次看到浏览器报错,先看状态码,就能大致判断问题出在哪。


六、HTTP/1.0 vs HTTP/1.1:连接管理的进化#

6.1 HTTP/1.0:每次请求都建新连接#

在 HTTP/1.0 中,每次请求都要建立一个新的 TCP 连接:

1
2
3
请求1 → 建立TCP → 发请求 → 收响应 → 断开TCP
请求2 → 建立TCP → 发请求 → 收响应 → 断开TCP
请求3 → 建立TCP → 发请求 → 收响应 → 断开TCP

🚗 类比:每次都打车回家

想象你上班路上要去三个地方——便利店、银行、超市。HTTP/1.0 的做法是:从家打车上便利店→下车→回家→再打车上银行→下车→回家→再打车上超市……累不累?

6.2 HTTP/1.1:持久连接(Keep-Alive)#

HTTP/1.1 引入了持久连接(Persistent Connection)

1
建立TCP → 请求1 → 响应1 → 请求2 → 响应2 → 请求3 → 响应3 → 断开TCP

一个 TCP 连接可以发送多个请求,大大减少了建立连接的开销。

🚗 类比:一趟搞定

还是上面的场景,HTTP/1.1 的做法是:从家出发→便利店→银行→超市→回家。一趟车全部搞定!

而且 HTTP/1.1 还支持流水线(Pipelining)——不用等第一个请求的响应回来,就可以发第二个请求。就像你点了外卖,还没送到就开始点下午的咖啡了。

6.3 HTTP/2 和 HTTP/3(了解即可)#

后面的版本继续优化:

  • HTTP/2:支持多路复用(一个连接上同时处理多个请求)、头部压缩、服务端推送
  • HTTP/3:基于 UDP(QUIC 协议),更快建立连接

💡 你的 nginx 支持哪个版本?

nginx 1.9.5+ 开始支持 HTTP/2。你可以查看 nginx 版本:

1
nginx -v

如果配置了 SSL,可以在 listen 443 ssl 后面加上 http2 来启用 HTTP/2。


七、Cookie:让”健忘的店员”记住你#

HTTP 是无状态的,但很多场景需要状态——登录、购物车、个性化推荐。怎么办?

Cookie 就是解决方案。

7.1 Cookie 的工作流程#

1
2
3
4
5
6
7
8
9
第一次请求:
客户端 ──────────────▶ 服务端

第一次响应:
客户端 ◀──────Set-Cookie: user=abc123; session=xyz789────── 服务端

后续请求:
客户端 ──Cookie: user=abc123; session=xyz789──▶ 服务端
(服务端看到 Cookie 就知道是你了)

7.2 Cookie 的组成#

Cookie 包含四部分:

  1. Cookie 头部行:请求中由客户端发送
  2. Set-Cookie 头部行:响应中由服务端设置
  3. Cookie 文件:客户端保存在本地的 Cookie
  4. 后端数据库:服务端保存的会话数据

🎫 生活类比:游乐园手环

你去游乐园玩(第一次访问网站):

  1. 入园时,工作人员给你一个彩色手环(服务端 Set-Cookie)
  2. 之后你玩每个项目,只要出示手环就行了(每次请求带上 Cookie)
  3. 手环上有编号,工作人员一看就知道你是 VIP 还是普通游客(服务端识别身份)
  4. 你可以把手环扔掉(清除 Cookie),下次入园重新领一个

7.3 Cookie 的隐私问题#

Cookie 也带来了隐私争议。因为服务端可以通过 Cookie 追踪你的行为——你在哪些页面停留、点击了什么广告、买了什么东西。这就是为什么很多网站会弹出”我们使用 Cookie”的提示——欧盟 GDPR 法规要求的。


八、Web 缓存:让访问快到飞起#

8.1 什么是 Web 缓存?#

Web 缓存(也叫代理服务器 / Proxy Server) 是一种用来存储最近访问过的资源副本的设备。

当用户请求一个资源时:

  1. 先查缓存——有没有?
  2. 有 → 直接返回(不用打扰原始服务器)
  3. 没有 → 缓存代替用户去原始服务器请求,拿到后存一份,再返回给用户

📦 生活类比:你桌面上的书架

你去图书馆借了一本书(请求网页),看完没还回去,放在桌上了(缓存)。下次想看,直接从桌上拿,不用再跑图书馆。但桌上空间有限(缓存容量),旧的不看了就得拿走(缓存淘汰)。

8.2 CDN:分布式缓存#

CDN(Content Delivery Network,内容分发网络) 就是一个分布在全球各地的 Web 缓存网络。

用户访问网站时,CDN 会把请求路由到离用户最近的节点,这个节点缓存了网站的内容。所以:

  • 上海用户访问 → 上海的 CDN 节点响应
  • 纽约用户访问 → 纽约的 CDN 节点响应

大大减少了延迟。

8.3 条件 GET(Conditional GET)#

缓存虽然好,但如果原始内容更新了怎么办?缓存的旧数据岂不是过时了?

条件 GET 解决了这个问题。缓存在请求原始服务器时,会带上一个时间戳:

1
2
GET /index.html HTTP/1.1
If-Modified-Since: Wed, 14 Jun 2026 09:00:00 GMT

意思是:”这个文件我上次拿到是 6 月 14 号的版本,有没有更新过?”

如果没更新,服务端返回 304 Not Modified(没有实体体,非常省带宽):

1
HTTP/1.1 304 Not Modified

如果更新了,返回 200 OK + 新内容。

你的博客也在用条件 GET

浏览器加载你的博客时,CSS、JS、图片这些静态资源会被缓存。下次访问时,浏览器会发送条件 GET,如果 nginx 发现文件没变,就返回 304——浏览器直接用本地缓存。这就是为什么你第二次打开同一个页面比第一次快很多。


九、实战:从请求到响应,你的博客经历了什么?#

让我们把今天学的所有知识串起来,完整走一遍你的博客从访问到展示的全过程:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
1. 用户在浏览器输入 bc970321.cn,回车

2. 浏览器构造 HTTP 请求:
GET / HTTP/1.1
Host: bc970321.cn

3. DNS 解析 bc970321.cn → 公网 IP(阿里云 ECS 的 IP)
(DNS 是明天的内容,今天先不管细节)

4. 浏览器与公网 IP:80 建立 TCP 连接(三次握手)

5. TCP 连接建立后,HTTP 请求通过 TCP 发送

6. 阿里云 ECS 上的 frps 收到请求,根据配置转发到 Mac mini

7. Mac mini 上的 frpc 把请求传给 nginx:8080

8. nginx 解析 HTTP 请求:
- 方法:GET
- 路径:/
- Host:bc970321.cn

9. nginx 找到对应的 Hexo 静态文件(index.html)

10. nginx 构造 HTTP 响应:
HTTP/1.1 200 OK
Content-Type: text/html
Content-Length: xxxxx

<!DOCTYPE html>...

11. 响应沿原路返回:nginx → frpc → frps → 互联网 → 浏览器

12. 浏览器解析 HTML,加载 CSS/JS/图片(每个资源又是新的 HTTP 请求)

13. 页面展示完成!用户看到了你的博客

整个过程涉及了今天讲的所有概念:C/S 架构、HTTP 请求/响应格式、TCP 连接、状态码、缓存。


🎯 关键知识点回顾#

概念 要点
应用层 协议栈顶层,直接为用户应用程序提供服务
C/S 架构 客户端发起请求,服务端响应,Web 的基本架构
P2P 架构 节点对等,既是客户端也是服务端
Socket 应用进程与网络之间的接口(”插座”)
HTTP Web 的核心协议,基于 TCP,无状态
HTTP 请求 请求行(方法+URL+版本)+ 头部行 + 实体体
HTTP 响应 状态行(版本+状态码+短语)+ 头部行 + 实体体
状态码 2xx成功 / 3xx重定向 / 4xx客户端错误 / 5xx服务端错误
持久连接 HTTP/1.1 默认开启,一个 TCP 连接处理多个请求
Cookie 解决 HTTP 无状态问题,实现用户识别和会话跟踪
Web 缓存 存储资源副本,减少延迟和带宽消耗
条件 GET 配合 If-Modified-Since,避免不必要的重传

📌 Day 4 预告:DNS 域名系统 & P2P 应用

今天我们多次提到”DNS 解析”,但故意跳过了细节——bc970321.cn 到底是怎么变成一个 IP 地址的?明天详细拆解 DNS 这个互联网的”通讯录”。同时介绍 P2P 文件分发,看看 BitTorrent 是怎么让下载越多人越快的。

明天见!📎


本文是基于《计算机网络:自顶向下方法》(第8版)的读书笔记,结合实际操作经验编写。