计算机网络:自顶向下方法 - Day 3 | 应用层与 HTTP 协议详解
📖 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 | 浏览器(客户端)──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 进程的地址#
要让两个进程通过网络通信,你需要知道两件事:
- 对方主机的 IP 地址:找到是哪台机器(就像小区地址)
- 对方的端口号:找到是机器上的哪个进程(就像门牌号)
🏠 类比你的博客
- 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 就是这个健忘的店员。你每次请求,它都当成第一次见面。
那网购购物车怎么记住我买了什么?——用 Cookie 和 Session。相当于你每次去都带一张”会员卡”,上面记录了你的信息。服务端看卡识别你,而不是靠记忆。
4.3 HTTP 与 TCP 的关系#
HTTP 使用 TCP 作为传输层协议(而不是 UDP):
1 | 应用层: HTTP |
TCP 为 HTTP 提供了可靠的数据传输服务。HTTP 不需要担心数据丢失、乱序、重复——这些 TCP 全帮你搞定了。
🚚 类比
HTTP 是寄件人,TCP 是快递公司。HTTP 只管写好信的内容,交给 TCP 就行。TCP 保证信件完整、按顺序送到对方手里。如果中途丢了,TCP 会自动重发。
HTTP 建立 TCP 连接需要经过”三次握手”(Day 5 会详细讲)。这意味着每个 HTTP 请求之前,都需要先花时间建立连接。
五、HTTP 请求与响应:逐行拆解#
这是今天的重头戏。让我们直接看真实的 HTTP 报文。
5.1 HTTP 请求报文#
当你在浏览器输入 bc970321.cn 并回车,浏览器发出的 HTTP 请求大概长这样:
1 | GET / |
逐行解读:
| 行 | 含义 |
|---|---|
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 | 200 OK |
结构分三部分:
- 状态行:
HTTP/1.1 200 OK(版本 + 状态码 + 状态短语) - 头部行:Server、Content-Type、Content-Length 等
- 实体体(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 | 请求1 → 建立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。
#
HTTP 是无状态的,但很多场景需要状态——登录、购物车、个性化推荐。怎么办?
Cookie 就是解决方案。
#
1 | 第一次请求: |
#
Cookie 包含四部分:
- Cookie 头部行:请求中由客户端发送
- Set-Cookie 头部行:响应中由服务端设置
- Cookie 文件:客户端保存在本地的 Cookie
- 后端数据库:服务端保存的会话数据
🎫 生活类比:游乐园手环
你去游乐园玩(第一次访问网站):
- 入园时,工作人员给你一个彩色手环(服务端 Set-Cookie)
- 之后你玩每个项目,只要出示手环就行了(每次请求带上 Cookie)
- 手环上有编号,工作人员一看就知道你是 VIP 还是普通游客(服务端识别身份)
- 你可以把手环扔掉(清除 Cookie),下次入园重新领一个
#
Cookie 也带来了隐私争议。因为服务端可以通过 Cookie 追踪你的行为——你在哪些页面停留、点击了什么广告、买了什么东西。这就是为什么很多网站会弹出”我们使用 Cookie”的提示——欧盟 GDPR 法规要求的。
八、Web 缓存:让访问快到飞起#
8.1 什么是 Web 缓存?#
Web 缓存(也叫代理服务器 / Proxy Server) 是一种用来存储最近访问过的资源副本的设备。
当用户请求一个资源时:
- 先查缓存——有没有?
- 有 → 直接返回(不用打扰原始服务器)
- 没有 → 缓存代替用户去原始服务器请求,拿到后存一份,再返回给用户
📦 生活类比:你桌面上的书架
你去图书馆借了一本书(请求网页),看完没还回去,放在桌上了(缓存)。下次想看,直接从桌上拿,不用再跑图书馆。但桌上空间有限(缓存容量),旧的不看了就得拿走(缓存淘汰)。
8.2 CDN:分布式缓存#
CDN(Content Delivery Network,内容分发网络) 就是一个分布在全球各地的 Web 缓存网络。
用户访问网站时,CDN 会把请求路由到离用户最近的节点,这个节点缓存了网站的内容。所以:
- 上海用户访问 → 上海的 CDN 节点响应
- 纽约用户访问 → 纽约的 CDN 节点响应
大大减少了延迟。
8.3 条件 GET(Conditional GET)#
缓存虽然好,但如果原始内容更新了怎么办?缓存的旧数据岂不是过时了?
条件 GET 解决了这个问题。缓存在请求原始服务器时,会带上一个时间戳:
1 | GET /index.html |
意思是:”这个文件我上次拿到是 6 月 14 号的版本,有没有更新过?”
如果没更新,服务端返回 304 Not Modified(没有实体体,非常省带宽):
1 | 304 Not Modified |
如果更新了,返回 200 OK + 新内容。
⚡ 你的博客也在用条件 GET
浏览器加载你的博客时,CSS、JS、图片这些静态资源会被缓存。下次访问时,浏览器会发送条件 GET,如果 nginx 发现文件没变,就返回 304——浏览器直接用本地缓存。这就是为什么你第二次打开同一个页面比第一次快很多。
九、实战:从请求到响应,你的博客经历了什么?#
让我们把今天学的所有知识串起来,完整走一遍你的博客从访问到展示的全过程:
1 | 1. 用户在浏览器输入 bc970321.cn,回车 |
整个过程涉及了今天讲的所有概念: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版)的读书笔记,结合实际操作经验编写。