📖 Day 15 / 15 · 《计算机网络:自顶向下方法》阅读笔记
每天 30 分钟,15 天读懂计算机网络。今天是最后一天——收官之日。我们把全书五层模型从头到尾串一遍,然后用 20 道面试题检验学习成果,最后以你的 Mac mini + nginx + frp + bc970321.cn 系统作为综合案例,把所有知识点焊死在脑子里。
读完这篇,你可以合上书,对自己说一句:「计算机网络,我入门了。」
bc's club
This is Bc's club
计算机网络:自顶向下方法 - Day 14 | 网络安全
📖 Day 14 / 15 · 《计算机网络:自顶向下方法》阅读笔记
每天 30 分钟,15 天读懂计算机网络。今天我们进入全书最「刺激」的一章——网络安全。前面 13 天我们学了网络怎么工作,今天来看看网络怎么被攻击、又怎么防御。加密、数字签名、SSL/TLS、防火墙、IDS/IPS,一次性安排上。你每天 SSH 登录服务器、frp 穿透内网、给网站配 HTTPS 证书,背后全是这些原理。
计算机网络:自顶向下方法 - Day 13 | 多媒体网络、流媒体、CDN 与 QoS
Day 13 — 多媒体网络、流媒体、CDN 与 QoS#
前情回顾:Day 11 我们聊了链路层和以太网,理解了 MAC 地址、CSMA/CD、VLAN 这些底层基石。至此,五层模型的每一层我们都已经翻了个遍。今天,我们换个视角——不再逐层拆解,而是聚焦一个改变互联网面貌的应用领域:多媒体网络。你每天刷的 B 站、追的 Netflix、开的腾讯会议,背后全是我们今天要聊的技术。
一、多媒体网络:互联网的”流量巨兽”#
1.1 多媒体流量到底有多恐怖?#
先来感受一组数据:
- YouTube 每分钟上传超过 500 小时的视频
- Netflix 占北美互联网流量的 30%+
- 2025 年全球 IP 流量中,视频流量占比超过 80%
可以说,今天的互联网基本就是一个巨大的视频管道。文本?那只是管壁上的一层薄漆。
生活类比:想象互联网是一条高速公路。早期只有几辆自行车(文本邮件),后来有了小汽车(网页图片),现在呢?全是重型卡车(视频流),一辆接一辆,日夜不停。
多媒体应用对网络的需求和传统应用截然不同:
| 特性 | 传统应用(HTTP、邮件) | 多媒体应用(视频、语音) |
|---|---|---|
| 延迟敏感度 | 不太敏感 | 极其敏感(>300ms 就难受) |
| 丢包容忍度 | 零容忍(文件不能缺字节) | 有一定容忍(丢几帧无所谓) |
| 带宽需求 | 低~中等 | 高~极高 |
| 持续时间 | 短连接为主 | 长时间持续流 |
这张表揭示了一个核心矛盾:多媒体要低延迟,但又需要大带宽;能容忍丢包,但 TCP 偏偏不丢包。这个矛盾贯穿了整个多媒体网络的设计。
二、流式存储视频:DASH 横空出世#
2.1 从”下载完再看”到”边下边看”#
远古时代(2000 年代初),在网上看视频的体验是这样的:点开链接 → 等待 → 等待 → 进度条慢慢爬 → 下完了 → 终于能看了。
这叫 下载-播放模型,显然不符合人类的即时满足需求。
于是有了 流式传输(Streaming):视频数据像流水一样源源不断地从服务器涌向你的设备,你一边接水一边喝,不需要等整个水池灌满。
生活类比:下载-播放就像你得把整个 bathtub 的水放满才能泡澡;流式传输就像淋浴——水一边来你一边洗,随到随用。
2.2 UDP 流 vs HTTP 流#
早期流媒体用 UDP(比如 RTSP 协议),因为 UDP 延迟低。但问题来了:
- 很多防火墙/NAT 会挡 UDP
- CDN 和缓存系统是围绕 HTTP 设计的
- UDP 没有拥塞控制,容易把网络搞崩
所以现代流媒体几乎全转向了 HTTP 流:用 TCP/HTTP 传视频,就像传普通网页一样。防火墙不挡,CDN 能缓存,皆大欢喜。
代价呢?TCP 的头部开销和重传机制会增加一点延迟。但对于非实时的流式视频(不是视频通话),这点延迟完全可以接受。
2.3 DASH:动态自适应流#
DASH(Dynamic Adaptive Streaming over HTTP) 是现代流媒体的核心技术。B 站、YouTube、Netflix 用的都是这个思路(具体实现可能是 HLS、MPEG-DASH 等变体)。
核心思想非常优雅:不要只准备一个版本的视频,准备很多个版本,让客户端自己选。
工作原理#
服务器把视频切割成等时长的小块(比如每块 2~10 秒),每个块编码成多个质量等级(不同码率、分辨率):
1 | 视频源 → 切分成 N 个块 |
同时服务器提供一个 manifest 文件(清单),列出了所有块和所有质量等级的 URL。
客户端(播放器)的工作流程:
- 先拉 manifest 文件,了解有哪些版本可选
- 根据当前带宽估计,选择一个合适的质量等级
- 请求第一个块 → 播放
- 持续监控带宽变化
- 如果带宽充裕 → 下一块选更高画质
- 如果带宽变差 → 降级到更低画质
这就是你在 B 站看视频时,画质自动在 1080p 和 360p 之间跳来跳去的原因。
生活类比:DASH 就像自助餐厅。厨师(服务器)把每道菜都做成了大份、中份、小份。你(客户端)根据自己的胃口(带宽)决定拿哪份。今天胃口好 → 大份牛排;突然胃疼 → 换小份沙拉。全程不用跟厨师说话,自己拿就行。
DASH 的聪明之处#
DASH 的精妙在于它把智能下放到了客户端:
- 服务器不需要知道客户端的网络状况
- 服务器只需要存一堆文件,用普通 HTTP 服务器就能搞定
- 客户端自己决定拉什么质量,完全自治
- 不同客户端可以有完全不同的体验,互不干扰
这也是为什么 CDN 非常喜欢 DASH——它就是一堆静态文件的 HTTP 请求,和缓存网页没有任何区别。
告警:码率切换的”突变”问题#
你可能注意过,B 站视频有时会在 1080p 和 360p 之间反复横跳,画面突然模糊又突然清晰,非常难受。
这是因为带宽估计不稳定。如果客户端的算法太激进,网络一抖动就降到最低画质,网络一恢复就冲到最高画质,用户体验就很糟糕。
优秀的 ABR(Adaptive Bit Rate)算法需要在稳定性和响应速度之间找平衡。Netflix 甚至用机器学习来优化这个决策过程。
三、CDN:内容分发网络#
3.1 为什么需要 CDN?#
假设你在中国上海,想看一个托管在美国纽约服务器的视频。数据要跨越太平洋,经过十几个路由器,延迟 200ms+。如果同时有一万个人在看这个视频,纽约服务器的带宽直接爆炸。
这个问题的解决方案不是把服务器搬到每个城市(成本太高),而是 CDN(Content Distribution Network)。
CDN 的核心思想:在全世界各地部署大量缓存节点(边缘服务器),把内容的副本放在离用户最近的地方。用户访问时,被引导到最近的节点,直接从那里获取数据。
生活类比:CDN 就像连锁超市。你不需要每次想吃薯片都去工厂总部(源服务器)买。工厂把薯片分销到你家门口的超市(CDN 节点),你走两分钟就能买到。库存没了?超市会让工厂补货。
3.2 CDN 的工作流程#
当你在浏览器输入 bc970321.cn 时(假设接了 CDN),发生的事情:
1 | 1. 用户请求 https://bc970321.cn/some-video.mp4 |
3.3 CDN 的关键设计挑战#
选哪个节点?#
CDN 的 DNS 需要决定把用户导向哪个节点。最简单的策略是基于地理距离,但距离不等于网络质量。一个北京用户可能到天津节点的延迟比到北京本地某节点还低(取决于运营商路由)。
高级 CDN(Cloudflare、Akamai、AWS CloudFront)使用:
- BGP Anycast:同一个 IP 地址在全球广播,用户的数据包自然路由到最近的节点
- 实时网络探测:持续测量各节点到各地区的延迟和丢包率
- 机器学习预测:根据历史数据预测最优节点
缓存什么?缓存多久?#
CDN 的缓存策略决定了命中率:
- Pull 策略:用户第一次请求时,节点从源服务器拉取并缓存。最常见。
- Push 策略:源服务器主动把热门内容推到所有节点。适合大文件首发的场景。
- TTL(Time To Live):缓存的有效期。过期后下次请求会重新从源服务器拉取。
对于你的博客 bc970321.cn:
- HTML 页面:TTL 短(几分钟),确保内容更新能及时生效
- 图片/CSS/JS 静态资源:TTL 长(几天到几个月),反正不怎么变
- 如果上了 Hexo 博客,可以用
hexo_asset_img等插件给静态资源加 hash 后缀,实现长缓存 + 自动更新
缓存替换策略#
边缘节点的存储空间有限,新内容来了,旧内容就得淘汰。常见策略:
- LRU(Least Recently Used):淘汰最久没用的
- LFU(Least Frequently Used):淘汰使用频率最低的
- 大多数 CDN 用 LRU 的变体
四、实战分析:bc970321.cn 博客接入 CDN#
说了这么多理论,来点实际的。假设我们要给 bc970321.cn 这个 Hexo 博客接入 CDN,怎么做?
4.1 方案一:全站 CDN(Cloudflare 免费版)#
最简单的方案——把整个域名丢给 Cloudflare:
1 | DNS 记录: |
Cloudflare 会自动:
- 缓存所有静态资源(CSS、JS、图片)
- 提供 DDoS 防护
- 提供 TLS 证书(免费)
- 全球 300+ 节点加速
缓存规则建议:
1 | # Cloudflare Page Rules |
4.2 方案二:静态资源走 CDN,动态请求走源站#
如果你想更精细地控制:
1 | DNS 记录: |
在 Hexo 模板里,把静态资源 URL 改成 CDN 地址:
1 | <link rel="stylesheet" href="https://cdn.bc970321.cn/css/style.min.css"> |
对 Hexo 来说,可以在 _config.yml 里设置:
1 | url: https://bc970321.cn |
4.3 Nginx 的流媒体能力#
你的服务器上跑着 Nginx,它其实也是流媒体的一把好手:
Nginx 做 HLS 直播/点播#
1 | # 点播 HLS |
Nginx 做 DASH#
1 | location /dash/ { |
Nginx 的限速功能(保护源站带宽)#
1 | # 限制每个 IP 的视频流带宽 |
这个 limit_rate_after 很聪明——先让你快速拿到前面的数据(快速起播),然后再限速(防止占太多带宽)。和 DASH 的思路异曲同工。
五、视频会议:实时多媒体的硬核战场#
5.1 和流媒体的区别#
流式视频(YouTube、B 站)是 存储视频——内容已经存在服务器上,可以提前编码、切分、缓存,对延迟的要求是”尽量低”但不是”必须低”。
视频会议(Zoom、腾讯会议、Discord)是 实时交互——画面是即时的,没有预编码和缓存的机会,延迟必须 < 300ms,否则人类就会觉得”卡”。
生活类比:流式视频像看录播的电视剧——你可以暂停、倒退、换清晰度。视频会议像打电话——你说完一句,对方 0.5 秒后才听到,这对话就没法正常进行了。
5.2 视频会议的架构#
经典架构:MCU(Multipoint Control Unit)#
早期视频会议用集中式服务器 MCU:
1 | 用户A ──┐ |
问题:MCU 服务器的计算压力极大(要解码+编码 N 路视频),延迟也高。
现代架构:SFU(Selective Forwarding Unit)#
WebRTC 时代的主流方案:
1 | 用户A ──发送多路质量的流──→ SFU ──→ 转发给 B 和 C(按需选择质量) |
SFU 不解码视频,只做选择性转发。它就像一个聪明的快递分拣员——不看包裹里是什么,只看标签决定转发给谁。
这和 DASH 的”多质量等级”思想一脉相承:发送端同时发多路不同质量的流(Simulcast),SFU 根据接收端的带宽选择转发哪一路。
5.3 WebRTC:浏览器的实时通信#
WebRTC 是浏览器内置的实时通信框架,核心技术包括:
- 音视频编解码:VP8/VP9、H.264、Opus 音频
- 传输协议:RTP over UDP(不走 TCP!实时性优先)
- NAT 穿透:ICE/STUN/TURN 协议组合拳
- 拥塞控制:自定义的 GCC(Google Congestion Control)
为什么 WebRTC 不用 TCP?因为实时通信中,迟到的帧不如丢弃的帧。如果用 TCP,一个丢包会导致后续所有数据被阻塞(队头阻塞),视频就卡住了。UDP 丢了就丢了,后面的帧照常到达,画面顶多闪一下。
六、RTP 与 RTCP:多媒体的传输协议#
6.1 RTP(Real-time Transport Protocol)#
既然视频会议用 UDP,但 UDP 啥都不保证,怎么知道包的顺序?怎么知道时间戳?这就需要 RTP。
RTP 运行在 UDP 之上,给每个包加上了:
- 序列号:检测丢包和重排序
- 时间戳:音视频同步
- 负载类型:标识编码格式(H.264、Opus 等)
- SSRC:标识发送源(多个人的流混在一起时区分谁是谁)
1 | RTP 包头: |
6.2 RTCP(RTP Control Protocol)#
RTP 只管传数据,但发送方需要知道:接收方收到了吗?网络质量怎么样?这就需要 RTCP。
RTCP 定期发送控制包,包含:
- 接收报告(RR):告诉发送方”我收到了多少、丢了多少、延迟抖动多大”
- 发送报告(SR):告诉接收方”我发了多少数据”
发送方根据 RTCP 的反馈来动态调整编码率和发送速率。这就是 网络自适应 的基础。
生活类比:RTP 是你不断往邮筒里塞信(数据包),RTCP 是收件人偶尔给你回一张明信片说”最近信件到达率 85%,有几封晚了 3 天”。你根据反馈调整寄信策略。
七、QoS:服务质量保障#
7.1 为什么需要 QoS?#
当网络拥塞时,所有流量一视同仁地丢包和延迟。但问题是:你的 SSH 会话丢几个包只是屏幕闪一下,而你的视频会议丢几个包就是”你说什么?我听不——“。
QoS(Quality of Service) 的目标就是:在网络资源有限时,优先保障重要流量的质量。
生活类比:QoS 就像医院的分诊制度。所有人都挤在急诊室(路由器队列),但心脏病发作的(实时语音)优先看,崴脚的(文件下载)慢慢等。
7.2 QoS 的关键技术#
调度策略(Scheduling)#
路由器在出口有多个队列,如何决定先发哪个?
- FIFO(First In First Out):先来先服务,默认策略,没有优先级
- Priority Queuing(优先级队列):高优先级队列先清空,低优先级等着。问题是低优先级可能被饿死
- Round Robin(轮询):轮流给每个队列发,公平但没优先级
- WFQ(Weighted Fair Queuing,加权公平队列):给每个队列分配权重,按比例分配带宽。最常用
1 | 权重分配示例: |
流量标记(DSCP / ToS)#
IP 包头有个字段叫 DSCP(Differentiated Services Code Point),6 个 bit,可以标记 64 个优先级。
路由器看到标记后,把包放进对应的队列。常见标记值:
EF(Expedited Forwarding):最高优先级,用于实时语音AF(Assured Forwarding):中等优先级,分多个子级别BE(Best Effort):默认,尽力而为
流量整形(Traffic Shaping)#
用 令牌桶(Token Bucket) 算法限制流的速率:
1 | 令牌桶原理: |
生活类比:令牌桶就像游乐场的过山车。每分钟放 10 个人上车(令牌生成速率 r),但排队区最多容纳 50 人(桶大小 b)。平时队不长,大家按 10 人/分钟上车。节假日突然来了 100 人?前 50 人排队,后面的人就得等——或者直接被劝退(丢包)。
主动队列管理:RED#
当队列太长时,所有包都经历高延迟。RED(Random Early Detection) 在队列快满之前就开始随机丢包,给发送方 TCP 一个”嘿,快堵了,慢点发”的信号。
这比等队列满了再全部丢包好得多——后者会导致 TCP 全局同步(所有连接同时减速,带宽利用率暴跌)。
7.3 QoS 的现实困境#
理论上 QoS 很美好,实践中却很难全面部署:
- 端到端问题:你的包要经过多个 ISP,每个 ISP 的 QoS 策略不同。你标记了 EF 优先级,别人的路由器可能直接忽略
- 资源分配之争:谁的语音通话比谁的重要?运营商之间很难达成一致
- 识别困难:Deep Packet Inspection(DPI)能识别流量类型,但加密(HTTPS/TLS)让 DPI 很难工作
所以现代方案更倾向于Provisioning(过度配置带宽)——带宽够用的话,QoS 的问题根本不存在。这也是为什么近年来 QoS 的讨论越来越少了——光纤入户、5G 时代,带宽不再是稀缺资源。
但对你的服务器来说,Nginx 的 limit_rate、limit_conn 就是一种轻量级 QoS,在带宽有限时仍然很有用。
八、内容分发的新趋势:P2P + CDN 混合#
8.1 P2P 辅助 CDN#
纯 CDN 的成本很高(每个节点都要服务器和带宽)。P2P-CDN 混合方案让用户之间互相分享数据,减轻服务器压力。
WebRTC 的 DataChannel 让浏览器之间可以直接传数据,不需要插件。这就催生了基于浏览器的 P2P-CDN:
1 | 传统 CDN: |
B 站和部分直播平台已经在用这种技术了。你在看直播时,你的浏览器其实在悄悄给其他观众传数据。
8.2 边缘计算#
CDN 节点不再只是缓存静态文件,而是在边缘节点运行代码(Cloudflare Workers、AWS Lambda@Edge)。这意味着:
- 视频转码可以在边缘完成
- 个性化推荐可以在边缘处理
- ABR 策略可以在边缘优化
九、完整链路:你发一条视频消息的全旅程#
把今天学的全部串起来。假设你在微信发一条 30 秒的视频消息给朋友:
1 | 1. 拍摄 → 手机编码(H.264/H.265 压缩,减少数据量) |
如果你俩在视频通话呢?流程完全不同:
1 | 1. 采集 → 摄像头/麦克风实时采集 |
同一个”视频”,因为场景不同,背后用的技术完全不一样。这就是计算机网络的美妙之处——没有银弹,只有适合场景的设计。
关键知识点回顾#
📌 流媒体核心概念#
| 概念 | 一句话总结 |
|---|---|
| 流式传输 | 边下边看,不需要下载完整文件 |
| DASH | 服务器切多质量等级的视频块,客户端按带宽自适应选择 |
| Manifest 文件 | 告诉客户端有哪些版本可选的”菜单” |
| ABR 算法 | 客户端决定下一块选哪个质量的核心算法 |
📌 CDN 核心概念#
| 概念 | 一句话总结 |
|---|---|
| CDN | 全球部署缓存节点,让用户从最近的节点获取内容 |
| DNS 重定向 | 通过 DNS 把用户引导到最近的 CDN 节点 |
| 缓存命中率 | 命中率越高,回源流量越少,性能越好 |
| 缓存策略 | Pull(被动拉取)、Push(主动推送)、TTL(有效期) |
📌 实时多媒体核心概念#
| 概念 | 一句话总结 |
|---|---|
| RTP | UDP 之上的实时传输协议,带序列号和时间戳 |
| RTCP | RTP 的控制协议,反馈网络质量 |
| WebRTC | 浏览器内置的实时通信框架 |
| SFU | 选择性转发单元,现代视频会议的核心架构 |
| Simulcast | 发送端同时发多质量等级的流 |
📌 QoS 核心概念#
| 概念 | 一句话总结 |
|---|---|
| QoS | 在有限资源下优先保障重要流量 |
| 调度策略 | FIFO / 优先级 / WFQ 决定队列发送顺序 |
| DSCP | IP 包头标记优先级的字段 |
| 令牌桶 | 限制流量速率的经典算法 |
| RED | 主动队列管理,避免全局同步 |
Day 14 预告:网络安全——加密、认证、SSL/TLS、防火墙 🔒#
今天我们聊了多媒体数据怎么高效传输,但有一个问题一直被我们忽略:这些数据在传输过程中安全吗? 你在咖啡厅连公共 WiFi 看视频,旁边的人能截获你的流量吗?你确定你在访问的是真正的 bc970321.cn 而不是钓鱼网站?
Day 14 我们进入计算机网络最关键的领域——安全。我们会聊:
- 对称加密 vs 非对称加密:一把钥匙还是两把钥匙?
- 数字签名与证书:怎么证明”我就是我”?
- SSL/TLS:HTTPS 背后的守护神
- 防火墙与 IDS/IPS:网络安全的门卫和监控摄像头
- VPN:在公共网络上建一条加密隧道
网络安全是整个计算机网络学习的收官之战,也是现实中最重要的话题。毕竟,一个不安全的网络,速度再快也没用。
“There are two types of companies: those that have been hacked, and those who don’t know they have been hacked.” — Robert Mueller
我们 Day 14 见!🚀
本文是《计算机网络:自顶向下方法》(第 8 版)读书笔记系列的第 13 篇。系列文章持续更新中。
计算机网络:自顶向下方法 - Day 12 | 无线网络与移动网络
📖 Day 12 / 15 · 《计算机网络:自顶向下方法》阅读笔记
每天 30 分钟,15 天读懂计算机网络。今天我们切断网线,聊聊无线网络和移动网络——WiFi(802.11)、蜂窝网络(4G/5G)、移动性管理和切换。
计算机网络:自顶向下方法 - Day 11 | 链路层与以太网
📖 Day 11 / 15 · 《计算机网络:自顶向下方法》阅读笔记
每天 30 分钟,15 天读懂计算机网络。今天我们「下沉」到数据链路层——数据在物理链路上实际传输的层。这里有以太网、ARP 协议、CSMA/CD、交换机和 VLAN。你每天插网线、连 WiFi,背后都是它们在默默工作。
计算机网络:自顶向下方法 - Day 10 | IP 协议详解与路由算法
📖 Day 10 / 15 · 《计算机网络:自顶向下方法》阅读笔记
每天 30 分钟,15 天读懂计算机网络。今天我们进入网络层的核心——IP 协议。IPv4、IPv6、子网掩码、CIDR、DHCP、NAT,再加上路由算法(链路状态、距离向量、OSPF、BGP),一次讲透。这是计算机网络中最「硬核」的一天,撑过去,后面的内容就是 downhill。
计算机网络:自顶向下方法 - Day 9 | 网络层概述、数据平面与路由器工作原理
Day 9 — 网络层概述、数据平面与路由器工作原理#
前情回顾:Day 7 我们聊了 UDP 和可靠数据传输原理,Day 8 深入 TCP 的拥塞控制。传输层的故事暂时告一段落——但你会发现,不管 TCP 还是 UDP,它们都建立在一个假设之上:网络层会把数据从源送到目的。这个”网络层”到底怎么送的?今天我们终于要打开这个黑盒,看看互联网的”快递分拣中心”——路由器——是怎么工作的。
一、网络层是个什么层?#
1.1 从传输层到网络层#
回顾一下我们的五层模型:
1 | 应用层 ← HTTP, DNS, SMTP... |
传输层的 TCP/UDP 负责端到端的通信(进程到进程),而网络层负责主机到主机的通信。你可以理解为:
- 传输层:你给快递员打电话说”请把这个包裹送到张三家,交给张三本人”(进程到进程,端口寻址)
- 网络层:快递分拣中心看地址,决定这个包裹要从北京发到上海(主机到主机,IP 寻址)
🎯 生活类比:传输层像是快递单上的收件人姓名和电话(具体到人),网络层像是收件地址里的城市和街道(具体到地方)。你写的快递单两层信息缺一不可,网络世界也一样。
1.2 网络层的两大功能#
网络层可以说是整个互联网最忙的一层,它有两个核心功能:
① 转发(Forwarding)——数据平面
当一个数据包到达路由器的某个输入端口,路由器要决定:这个包该从哪个输出端口送出去?这个决策是本地的、即时的,就在路由器内部完成。
② 路由(Routing)——控制平面
路由器们需要互相交流,搞清楚整个网络的拓扑结构,然后在每台路由器上建立起转发表(Forwarding Table),告诉转发逻辑”遇到去某个网络的包,应该从哪个端口送出去”。这个决策是全局的、慢速的,需要路由协议(如 OSPF、BGP)的参与。
| 对比项 | 转发(数据平面) | 路由(控制平面) |
|---|---|---|
| 时间尺度 | 纳秒级(每个包都要处理) | 秒~分钟级(拓扑变化才更新) |
| 作用范围 | 单个路由器内部 | 整个网络/自治系统 |
| 关键设备 | 路由器的硬件/软件 | 路由协议和算法 |
| 类比 | 快递员看地址分拣包裹 | 快递公司规划物流线路 |
🎯 生活类比:想象一个巨大的快递分拣中心。转发就是传送带上的包裹到了一个分叉口,工作人员看一眼邮编,推到对应的滑道里——这个动作每秒发生几百万次。路由则是公司的调度部门根据全国道路情况、新开的网点、拥堵的高速,定期更新一份”路由指南”发到每个分拣中心——这个动作可能一天才更新一次。
1.3 数据平面 vs 控制平面#
这是第 8 版书里特别强调的一个架构视角:
1 | ┌─────────────────────────────────────────┐ |
数据平面是路由器的”肌肉”——快速、机械、每包执行。通常用硬件(ASIC、FPGA)实现,追求速度。
控制平面是路由器的”大脑”——思考、协商、更新。通常用软件实现,运行路由协议进程。
传统路由器把这两个功能合在一台设备里。但 SDN(Software-Defined Networking,软件定义网络)的革命性思想就是:把控制平面从路由器里抽出来,放到一个集中的控制器上。就像连锁快餐店把菜单决策权从每家门店收回总部——门店只管按菜单做菜(转发),总部负责研发新品和调整策略(路由)。
🔧 实战关联:你家跑 OpenWrt 的路由器就是一个完整的传统路由器。OpenWrt 里
ip route显示的 routing table 就是控制平面维护的转发表,而路由器芯片内部的硬件转发芯片(如 MediaTek/Qualcomm 的 SoC)就是数据平面。当你跑tc qdisc做流量整形时,你其实是在数据平面上动手脚!
二、路由器的”解剖图”#
现在让我们打开路由器的外壳,看看里面长什么样。一个通用的路由器架构如下:
1 | 输入端口 交换结构 输出端口 |
路由器由四大组件构成:
- 输入端口(Input Ports)
- 交换结构(Switching Fabric)
- 输出端口(Output Ports)
- 路由处理器(Routing Processor)
我们逐一拆解。
三、输入端口:门口的”安检+导航员”#
3.1 输入端口的三层结构#
输入端口不止是一个”网线口”那么简单。它从物理层到网络层都有处理逻辑:
1 | ┌──────────────────────────────────┐ |
🎯 生活类比:输入端口就像公司大楼的前台。物理层是保安检查你的车有没有停对位置,链路层是前台确认你是不是从合作公司来的(链路层帧),网络层是前台查访客系统,决定你应该去几楼哪个会议室。
3.2 最长前缀匹配(Longest Prefix Matching)#
这是输入端口网络层的核心工作。转发表大概长这样:
| 目的网络前缀 | 输出接口 | 下一跳 |
|---|---|---|
| 11001000.10101010.00000000.00 | 接口0 | — |
| 11001000.10101010.10100000.00 | 接口1 | — |
| 11001000.10101010. | 接口2 | — |
| 0.0.0.0/0(默认路由) | 接口3 | 网关 |
当一个目的 IP 为 192.168.160.1(11001000.10101010.10100000.00000001)的包到达时,路由器需要在表里找最匹配的那一条。
- 第一条
...00000000.00/26不匹配 - 第二条
...10100000.00/26匹配!26 位前缀 - 第三条
192.168.x.x/16也匹配,但只有 16 位前缀
最长前缀匹配的规则是:选择匹配位数最多的那条——这里是第二条(26 位 > 16 位),从接口 1 输出。
🎯 生活类比:你寄快递写的地址是”北京市海淀区中关村大街1号”。快递分拣中心有一张表:
- “北京市” → 发到北京转运中心
- “北京市海淀区” → 发到海淀分部
- “北京市海淀区中关村” → 发到中关村网点
三个都匹配,但当然选最精确的那个——最长的前缀匹配!
3.3 为什么要在输入端口做查找?#
你可能会问:为什么不把包直接送给路由处理器(CPU)去查表?因为太慢了!
现代骨干路由器每个端口要处理 100Gbps 甚至 400Gbps 的流量。每个包的转发决策必须在纳秒级完成。如果每个包都走 CPU 查表,CPU 早就累趴了。所以输入端口上有专用的硬件查找引擎(TCAM,三态内容寻址存储器),一个时钟周期就能完成查找。
🔧 实战关联:在 OpenWrt 上跑
ip route get 8.8.8.8,你看到的就是路由查找的结果。你的家用路由器虽然速度不比骨干路由器,但原理一样——每个到达的包都要走一遍这个查找过程。当你用 frp 做内网穿透时,frp 服务器收到的数据包要转发到你内网的服务,这个过程中经过的每一个路由器都在做最长前缀匹配。
四、交换结构:路由器的”十字路口”#
查完表,知道了包该从哪个输出端口出去,接下来就是把包从输入端口送到输出端口。这个”搬运”动作就是交换结构要干的事。
有三种经典的交换方式:
4.1 经内存交换(Switching via Memory)#
最早的路由器其实就是一台普通计算机,用 CPU 来”交换”:输入端口把包写入内存,CPU 再从内存读出来写到输出端口。
1 | 输入端口 ──▶ [ 内存 ] ──▶ 输出端口 |
优点:简单,用现成的硬件就行。
缺点:慢!每个包要两次内存访问(读+写),而且 CPU 要介入。如果内存带宽是 B,那最大吞吐量是 B/2。而且 CPU 处理速度有限。
🎯 生活类比:就像你一个人在家里搬东西。左手从门口拿进来放桌上(写内存),右手再从桌上拿到后门口递出去(读内存)。一个人两手交替,速度可想而知。
4.2 总线交换(Switching via Bus)#
输入端口把包放到一条共享总线上,输出端口从总线上取走。
1 | 输入端口0 ──┐ |
优点:比内存交换快,不需要 CPU 介入。
缺点:总线是共享的!同一时刻只能有一个端口往总线上放数据。如果 N 个输入端口同时有包要送,只有一个能成功,其他要等。总线带宽成了瓶颈。
🎯 生活类比:公司只有一部电梯。每层楼的人都要用它下楼。同一时间只能载一批人,大家只能排队等。楼层多了就堵成一锅粥。
大多数家用路由器(包括 OpenWrt 设备)用的就是总线交换架构。因为家用场景流量不大,总线够用了。但在骨干网,总线交换就力不从心了。
4.3 互联网络交换(Switching via Interconnection Network)#
这是高性能路由器用的方案。用一个多级的交叉开关矩阵(Crossbar Switch),让多个输入-输出对同时交换数据。
1 | 输出0 输出1 输出2 输出3 |
如果输入端口0要送到输出端口0,同时输入端口1要送到输出端口1,这两个交换可以同时进行,互不干扰。只有当两个输入端口要送到同一个输出端口时才会冲突。
优点:可以并行交换,吞吐量高,是现代骨干路由器的标配。
缺点:复杂、贵、功耗大。N×N 的 crossbar 需要 N² 个交叉点。
🎯 生活类比:从”一部电梯”升级到了”立交桥”。每条路都有自己的匝道,想去哪直接走对应的匝道,互不干扰。只有当两辆车想去同一个出口时,才需要排队。
4.4 交换结构的性能瓶颈#
无论哪种交换方式,当多个输入端口的包都想去同一个输出端口时,就会出现拥塞(HOL Blocking,Head-of-Line Blocking)。排在队头的包如果因为输出端口冲突而被阻塞,后面即使目的地是空闲端口的包也出不去——这就是队头阻塞问题。
想象你在超市收银台排队。你前面的大哥在找零钱(输出端口忙),你后面的小姐姐其实只是想退个货(另一个收银台空闲),但她排在你后面,只能等你——这就是队头阻塞。
五、输出端口:出门前的”排队等候区”#
5.1 输出端口的结构#
输出端口是包离开路由器前的最后一站:
1 | ┌──────────────────────────────────┐ |
5.2 为什么要排队?#
排队的根本原因:到达速率 > 输出链路的发送速率。
想象一个水槽。进水管的水流(到达的包)大于出水管的水流(输出链路带宽),水就会在槽里积攒。这就是排队。
1 | 到达的包 输出链路 |
当队列满了,新来的包就只能被丢弃(Drop)或者挤掉已有的包(如果用了 AQM 主动队列管理)。
5.3 排队场景#
考虑两种排队场景:
场景一:输入排队
当交换结构来不及处理所有输入端口的包时,包在输入端口排队。前面提到的 HOL 队头阻塞就是输入排队的主要问题。
场景二:输出排队
当多个输入端口的包同时到达同一个输出端口时(交换结构足够快,但输出链路只有一条),包在输出端口排队。
实际路由器中,输出排队更常见,因为现代 crossbar 交换结构速度已经足够快,瓶颈通常在输出链路带宽上。
5.4 队列管理策略#
队列满了怎么办?有几种策略:
① 尾丢弃(Tail Drop,FIFO/DropTail)
最简单的策略:队列满了,新来的包直接丢弃。
1 | 队列状态:[包A][包B][包C][包D][包E] ← 满了! |
缺点:会导致”全局同步”问题——TCP 流同时感知到丢包,同时降低发送速率,然后同时增大,导致网络流量呈锯齿状波动。
② 随机早期检测(RED, Random Early Detection)
在队列还没满的时候就开始以一定概率随机丢包,给 TCP 发送方”温柔”的信号:”快了快了,悠着点”。
1 | 队列长度 < min_threshold → 不丢包 |
RED 的聪明之处在于:在崩溃之前就发预警,而不是等到队列满了才暴力丢包。
🎯 生活类比:尾丢弃就像高速收费站在车已经堵了一公里后才关闭入口。RED 则是在车排队到 200 米时就放一块”前方拥堵,建议绕行”的牌子,让一部分车主动分流。
③ 公平排队(Fair Queuing)
不同流轮流发送,避免一个贪婪的流量霸占整个队列。
🎯 生活类比:银行窗口排队,每个人限办 3 分钟,时间到了下一位。不会因为前面有个大哥办 50 张银行卡的复杂业务就让你等一下午。
5.5 调度策略(Scheduling)#
当输出端口有多个包排队时,谁先走?这就是调度器要回答的问题。
| 调度策略 | 规则 | 特点 |
|---|---|---|
| FIFO(先来先服务) | 按到达顺序发送 | 最简单,但无法区分优先级 |
| 优先级排队 | 高优先级先发 | 可能导致低优先级饿死 |
| 加权公平排队(WFQ) | 按权重分配带宽 | 各流公平,可保证最低带宽 |
| 缺口轮转(Round Robin) | 轮流给每个流发送机会 | 简单的公平 |
🔧 实战关联:OpenWrt 的 SQM(Smart Queue Management)就是一个典型的输出端口队列管理 + 调度系统。当你在家开 Zoom 会议的同时下载大文件,SQM 通过 CAKE 或 fq_codel 算法,保证 Zoom 的包(低延迟需求)优先发送,而下载流量(吞吐量需求)排队等候。这背后就是今天讲的排队管理和调度策略!
六、把所有部件串起来:一个包的路由器之旅#
让我们追踪一个 IP 数据包穿过路由器的完整过程:
1 | 1. 包从物理链路到达输入端口 X |
🎯 完整类比:想象你是一封信。
- 邮递员把你从信箱里取出来(物理层接收)
- 分拣员检查信封上的地址(链路层解封装 + 网络层查表)
- 分拣员决定你该发往哪个城市的分拣中心(最长前缀匹配 → 输出端口)
- 你被放到传送带上,送到了正确的发货口(交换结构)
- 发货口前面排了很多信,你在队列里等了一会儿(输出排队)
- 终于轮到你了,你被装到邮车里出发了(调度发送)
- 到了下一个分拣中心,重复以上过程……
一封从北京寄到上海的信,中间可能经过 5-10 个分拣中心。一个从你的电脑发到 B 站服务器的数据包,中间通常也要经过 5-15 台路由器。每经过一台路由器,就叫一跳(hop)。
七、实战分析:你家路由器的一天#
7.1 OpenWrt 路由器 = 小型路由器#
你家里的 OpenWrt 路由器就是一个麻雀虽小五脏俱全的路由器:
1 | # 查看路由表(控制平面的产物) |
- 输入端口:WAN 口(eth0)和 LAN 口(br-lan,其实是一个 bridge)
- 交换结构:多数用总线交换(SoC 集成)
- 输出端口:同上,WAN/LAN 口复用
- 路由处理器:路由器的 CPU(可能就是一个小小的 ARM 核)
- 转发表:
ip route看到的路由表
7.2 frp 数据转发的路由过程#
当你用 frp 做内网穿透时,数据包经过了哪些路由器?
1 | 你的服务器(内网 192.168.1.100) |
每一跳,路由器都在做同样的事:
- 收到包,看目的 IP
- 最长前缀匹配,决定下一跳
- 通过交换结构送到对应端口
- 从输出端口发出去
frp 服务器收到包后,解封装,看到的是你内网服务的应用层数据(比如 HTTP 请求),然后再转发给目标服务。从网络层的角度,frp 服务器只是路径上的一个中间节点,但它同时也是一个应用层代理——既走路由器的活(网络层转发,不过是通过应用层代理实现的),又做应用层的事。
💡 有趣观察:frp 本质上是在应用层模拟了网络层的转发功能。真正的路由器在 IP 层转发,而 frp 在 TCP/应用层转发。这就像快递公司(IP 路由)和跑腿小哥(frp 代理)都能帮你送东西,但快递公司走的是自动化分拣流水线,跑腿小哥走的是”人肉转交”——速度和效率不可同日耳语,但跑腿小哥能送的”东西”更灵活。
八、路由器的性能指标#
8.1 吞吐量#
路由器的吞吐量是指单位时间内能处理的数据量。这取决于三个环节中最慢的那个:
- 输入端口处理速度
- 交换结构带宽
- 输出端口发送速度
木桶效应——哪个环节最慢,整个路由器的吞吐量就被限制在哪里。
8.2 延迟#
一个包穿过路由器的时间包括:
- 处理延迟:查表、检查首部(通常微秒级)
- 排队延迟:在输出队列等待的时间(取决于拥塞程度,从 0 到毫秒级不等)
- 交换延迟:通过交换结构的时间(通常纳秒级)
注意,路由器的延迟主要来自排队。如果你家的网速测试显示延迟很高,很多时候不是路由器处理慢,而是队列太长了!
8.3 丢包率#
当队列满了,包被丢弃。丢包率是衡量路由器拥塞程度的重要指标。TCP 会根据丢包调整发送速率——这就是 Day 8 讲的 TCP 拥塞控制的基础。现在你知道了:TCP 感知到的”丢包”,本质上很多时候就是路由器输出端口队列满了之后的尾丢弃(或 RED 丢弃)。
💡 恍然大悟时刻:TCP 拥塞控制和路由器排队管理其实是一个闭环系统。路由器丢包 → TCP 减速 → 路由器队列变短 → 不再丢包 → TCP 加速 → 路由器队列又变长 → 又开始丢包……如此循环。理解了这个循环,你就理解了互联网流量自我调节的核心机制。
关键知识点回顾#
让我们用一张表总结今天的内容:
| 概念 | 要点 |
|---|---|
| 网络层两大功能 | 转发(数据平面,本地快速决策)+ 路由(控制平面,全局拓扑计算) |
| 数据平面 vs 控制平面 | 数据平面硬件实现每包转发,控制平面软件实现路由协议;SDN 将二者分离 |
| 路由器四大组件 | 输入端口、交换结构、输出端口、路由处理器 |
| 输入端口核心 | 最长前缀匹配查找,决定输出端口 |
| 交换方式 | 内存交换(慢)、总线交换(中等)、互联网络/crossbar(快) |
| 输出端口核心 | 排队管理(DropTail、RED)+ 调度策略(FIFO、优先级、WFQ) |
| 排队位置 | 主要在输出端口排队(现代交换结构足够快) |
| HOL 阻塞 | 输入排队时的队头阻塞问题 |
| 队列管理策略 | 尾丢弃(简单但导致全局同步)、RED(主动预警)、公平排队 |
| TCP 与队列的闭环 | 路由器丢包 → TCP 减速 → 队列缩短 → TCP 加速 → 循环 |
一句话总结:路由器就是一个高性能的”快递分拣中心”——输入端口看地址(最长前缀匹配),交换结构搬运包裹(crossbar 并行交换),输出端口排队发货(排队管理+调度),路由处理器在后台规划路线(路由协议)。
Day 10 预告#
今天我们了解了路由器的硬件架构和工作原理,但有一个关键的东西还没深入:路由器查的那张转发表是怎么来的? 路由器怎么知道去往某个网络该走哪条路?这涉及到:
- IP 协议详解:IPv4 数据报格式、分片与重组
- IPv6:为什么需要 IPv6?IPv4 和 IPv6 有什么区别?
- DHCP:你的电脑是怎么自动获得 IP 地址的?
- NAT:你家一个公网 IP,多台设备怎么上网的?
- 路由算法:链路状态(LS)和距离矢量(DV)算法是怎么算出最短路径的?
- OSPF 与 BGP:互联网的两 大路由协议
Day 10,我们将深入 IP 协议的细节,看看那个 192.168.1.1 背后的故事。咱们不见不散!
📖 读书进度:《计算机网络:自顶向下方法》第 8 版 第 4 章(4.1-4.2 节)
下期见 👋
计算机网络:自顶向下方法 - Day 8 | TCP 协议详解与拥塞控制
📖 Day 8 / 15 · 《计算机网络:自顶向下方法》阅读笔记
每天 30 分钟,15 天读懂计算机网络。今天我们来深入互联网最重要的传输层协议——TCP。三次握手、四次挥手、可靠传输、流量控制、拥塞控制,一次讲透。
计算机网络:自顶向下方法 - Day 7 | UDP 协议详解与可靠数据传输原理
Day 7 — UDP 协议详解与可靠数据传输原理#
前情回顾:Day 6 我们聊了可靠数据传输的”甲方”——网络层只提供”尽力而为”的服务,丢包、乱序、比特出错全不管。那传输层怎么办?今天我们就来认识两位选手:一位是”摆烂大师” UDP,另一位是”完美主义者”——可靠数据传输协议(rdt 系列模型)。
一、UDP:我就主打一个”信不信由你”#
1.1 UDP 是什么?#
UDP(User Datagram Protocol,用户数据报协议),是传输层最朴素的协议。它做的事儿少到令人发指:
- 多路复用/多路分解:把数据送到正确的应用进程(通过端口号)
- 差错检测:可选的校验和(checksum)
- 没了
就这两件事。不保证交付,不保证顺序,不保证不出错。你发出去的数据包,它生不生死不死,UDP 拒不负责。
🎯 生活类比:UDP 就像街头发的传单。发的人(发送方)往街上一撒就走了,你收到没收到?传单被风吹走了?发的人根本不在乎。而 TCP 像是挂号信,必须签收、必须确认。
1.2 UDP 数据报首部格式#
UDP 首部只有 8 个字节,小得可怜:
1 | 0 7 8 15 16 23 24 31 |
| 字段 | 大小 | 说明 |
|---|---|---|
| 源端口 | 2 字节 | 发送方端口号(可选,全 0 表示不需要回复) |
| 目的端口 | 2 字节 | 接收方端口号 |
| 长度 | 2 字节 | 整个 UDP 数据报(首部+数据)的字节数 |
| 校验和 | 2 字节 | 差错检测(IPv4 中可选,IPv6 中必须) |
就这么简单。首部开销 8 字节,相比之下 TCP 首部 20 字节起步,UDP 省下来的带宽在高速场景下相当可观。
1.3 UDP 校验和#
虽然 UDP “摆烂”,但它还是提供了一个校验和机制——这就像摆烂的人出门前还是看了一眼天气。
原理:发送方将数据按 16 位为一个单位,做反码求和(one’s complement sum),结果取反得到校验和。接收方收到后,把所有 16 位数据和校验和一起做反码求和,如果全为 1,说明没出错。
注意:校验和只能检测错误,不能纠正错误。发现错误后怎么办?直接丢掉。没错,UDP 的纠错策略就是——扔。
🎯 生活类比:校验和就像快递箱上的”易碎品”贴纸。快递员(网络)看到这个标签,如果发现箱子破了,他不会帮你修补,而是直接扔掉。至于你知不知道快递丢了……那不是快递员的事。
1.4 为什么应用层要”选择”UDP?#
既然 UDP 这么不靠谱,为什么还有应用要用它?因为”不靠谱”换来了”高效率”:
| 优势 | 说明 |
|---|---|
| 无连接 | 不需要握手,想发就发 |
| 低开销 | 8 字节首部,省带宽 |
| 无拥塞控制 | 发送速率不受网络拥塞影响 |
| 实时性好 | 延迟低,适合实时应用 |
常见使用 UDP 的应用:
- DNS:查询就一个请求一个响应,握手纯属浪费时间
- SNMP:网络管理,偶尔丢个数据包无所谓
- 流媒体 / 视频会议:实时性 > 可靠性,丢几帧画面可以接受
- 游戏:你不想因为一个丢包就让整个游戏卡住等重传吧?
- QUIC:HTTP/3 的底层协议,在 UDP 上构建了自己的可靠性(取 UDP 之长,补 UDP 之短)
1.5 实战:DNS 查询中的 UDP#
在你的实际系统中,每次你打开一个网页、解析一个域名,背后都是 UDP 在工作。来看一个真实的 DNS 查询:
1 | 你的电脑 → DNS服务器:UDP报文(目的端口 53) |
整个过程没有握手,没有建立连接,一发一收,干净利落。如果丢了?DNS 客户端会超时重试(这是应用层的事,UDP 不管)。
你可以用 tcpdump 或 Wireshark 实际抓包看看:
1 | sudo tcpdump -i any port 53 -n |
你会看到所有 DNS 查询都是 UDP,快速且高效。
💡 思考题:为什么 DNS 不用 TCP?答案:DNS 查询通常很小(一个请求一个响应),建立 TCP 连接的三次握手开销太大。UDP 直接发出去,效率高得多。不过当 DNS 响应数据超过 512 字节时,会切换到 TCP(通过 EDNS0 或区域传输)。
1.6 UDP 的可靠性补救方案#
UDP 本身不可靠,但很多应用在 UDP 上构建了自己的可靠性机制:
| 应用 | 可靠性策略 |
|---|---|
| QUIC | 在 UDP 上实现了类似 TCP 的可靠性 + 加密 |
| TFTP | 停等协议,每个数据包都要 ACK |
| 你的 frp | UDP 心跳 + 超时重连机制 |
这就是 UDP 的精髓——它给你最大的自由度,可靠性你自己加。
二、可靠数据传输原理:从零造一个可靠协议#
现在进入今天的重头戏。网络层(IP 层)是不可靠的:数据包可能丢失、损坏、乱序。但传输层需要给应用层提供可靠的数据传输。
问题来了:如何在不可靠的信道上实现可靠的数据传输?
这就是 rdt(reliable data transfer)协议系列要解决的问题。教材用了渐进式的方法,一步步从最简单的版本演进到接近 TCP 的模型。
🎯 生活类比:你在网上下单买了个东西,快递可能丢、可能损坏、可能寄错地址。你(发送方)和卖家(接收方)需要一套规则,确保你最终拿到正确的东西——这就需要确认、重传、序号等机制。
2.1 rdt 1.0:完美世界(理想信道)#
假设:底层信道完全可靠——不丢包、不出错、不乱序。
这种情况下,可靠传输简单到离谱:
- 发送方:
rdt_send(data)→ 把数据发出去(udt_send(packet)) - 接收方:
rdt_rcv(packet)→ 把数据取出来,交给上层
不需要确认,不需要重传,不需要序号。完美!
当然,现实中的网络从来不完美,所以这只是一个起点。
2.2 rdt 2.0:引入 ARQ(有比特错误的信道)#
假设:底层信道可能发生比特错误(0 变成 1),但不会丢包。
这是个更现实的场景。如果数据在传输中出了错,接收方需要能检测到错误(通过校验和),然后告诉发送方重传。这就是 ARQ(Automatic Repeat reQuest,自动重传请求) 协议。
rdt 2.0 引入了三个机制:
- 差错检测:校验和
- 接收方反馈:ACK(确认正确)或 NAK(确认有错)
- 重传:收到 NAK 就重传
发送方状态机:
1 | [等待上层数据] |
接收方状态机:
1 | [等待下方数据] |
🎯 生活类比:你网购收到快递,当面拆开检查。东西没问题就说”OK”(ACK);发现坏了就说”退换”(NAK)。卖家收到 NAK 就重新发一个。
rdt 2.0 的致命缺陷#
如果 ACK 或 NAK 本身在传输中出了错怎么办?发送方收到的反馈不可靠!
比如发送方收到一个损坏的 ACK,它不知道这是 ACK 还是 NAK,也不知道接收方到底有没有正确收到数据。
rdt 2.1:加序号#
解决方案:给数据包加序号(0 或 1,交替使用)。这样即使 ACK/NAK 出错,发送方也能通过序号判断:
- 如果收到对”当前序号”的 ACK → 数据被正确接收,发下一个
- 如果收到对”上一个序号”的 ACK 或收到 NAK → 重传当前包
发送方状态:
- 等待上层调用 0(准备发序号 0 的数据)
- 等待 ACK 0(等序号 0 的确认)
- 等待上层调用 1
- 等待 ACK 1
接收方也能通过序号判断:这是新数据(序号变化)还是重传(序号和上次一样)?
🎯 生活类比:卖家发货时在快递上标注”第 1 单”、”第 2 单”。你收到后回复”第 1 单 OK”。即使你的回复在路上被弄模糊了,卖家看到”第 1 单”的字样就知道第一单没问题了。
rdt 2.2:去掉 NAK#
rdt 2.2 是 2.1 的优化版——用 ACK 携带序号代替 NAK。
如果接收方发现数据出错,不发 NAK,而是发送上一个正确接收包的 ACK(即”重复 ACK”)。发送方收到重复 ACK 就知道需要重传。
这样做的好处:反馈消息只有一种类型(ACK),简化了协议。
🎯 生活类比:你不再说”这个坏了”(NAK),而是重复说”上一个好的”(重复 ACK)。卖家发现你说了两次”第 1 单 OK”,就知道第 2 单出了问题。
2.3 rdt 3.0:处理丢包(现实世界)#
假设:底层信道不仅会出错,还会丢包。
这是最接近真实互联网的情况。如果数据包或 ACK 丢了,发送方会一直等下去,永远收不到回复——这就是死锁。
解决方案:超时重传。
发送方在发送数据后启动一个定时器。如果在超时前没收到 ACK,就认为数据丢了,重传。
rdt 3.0 的发送方状态机:
1 | [等待上层调用 0] |
这个协议被称为停等协议(Stop-and-Wait)——发送方发一个包,停下来等 ACK,收到确认后再发下一个。
🎯 生活类比:你给朋友发微信,发完之后看表。如果 5 分钟没回,你就重发一遍。虽然原始,但能保证可靠性。
停等协议的性能问题#
停等协议虽然可靠,但效率极低。考虑以下场景:
- 数据包大小:1 KB
- 链路带宽:1 Gbps
- 往返时延(RTT):100 ms
发送一个包的实际传输时间:1KB / 1Gbps ≈ 8 μs
但发送方要等 RTT(100 ms)才能发下一个包。
信道利用率 = 8μs / (8μs + 100ms) ≈ 0.008%
这个效率简直惨不忍睹!就好比你有一辆法拉利(高带宽链路),但每开一米就要停下来等红灯(等 ACK),大部分时间都在等。
这就引出了我们今天最后一个重要话题——流水线协议。
三、流水线协议:从”一个一个来”到”批量发送”#
3.1 核心思想#
停等协议的问题在于:发送方只允许”在路上”存在一个未确认的数据包。如果我们允许同时有多个未确认的数据包在路上,就能大幅提高信道利用率。
这就是流水线(pipelining)技术。
流水线需要两个新机制:
- 序号范围扩大:不再是 0/1 交替,而是每个包有唯一的序号(比如 0, 1, 2, …, N)
- 缓冲:发送方需要缓存已发送但未确认的包(以备重传),接收方可能需要缓存乱序到达的包
🎯 生活类比:你不再是一个一个发快递、收到确认再发下一个。而是一次发出去 10 个快递(流水线),然后等确认。效率直接拉满。
流水线协议有两种经典实现:回退 N 步(GBN) 和 选择重传(SR)。
3.2 回退 N 步(Go-Back-N, GBN)#
核心规则#
GBN 允许发送方在不等待确认的情况下连续发送最多 N 个数据包(N 称为”窗口大小”)。但它的容错策略比较粗暴——只要有一个包丢了,从那个包开始后面全都要重传。
关键参数:
- 窗口大小 N:最多允许 N 个未确认的包在途
- 基号 base:最早未确认的包的序号
- 下一个序号 nextseqnum:下一个要发的包的序号
窗口范围:[base, base + N - 1]
1 | 已确认 窗口内(已发送未确认) 不能发 |
发送方行为#
收到上层新数据:
- 如果 nextseqnum < base + N → 在窗口内,发送数据,nextseqnum++
- 如果 nextseqnum >= base + N → 窗口满了,拒绝或缓存
收到 ACK n:GBN 使用累积确认——ACK n 表示”序号 n 及之前的所有包都正确收到了”。收到 ACK n 后,base = n + 1(窗口滑动)
超时:只对最早的未确认包(base)启动一个定时器。超时后,重传从 base 开始的所有未确认包
接收方行为#
GBN 接收方也很简单:只按序接收。
- 期望序号 expected_seqnum
- 收到正确序号的包 → 提取数据,发 ACK,expected_seqnum++
- 收到乱序包 → 直接丢弃,只重发最后一个正确包的 ACK
🎯 生活类比:你在食堂排队打饭。你期望第 1 号、第 2 号、第 3 号菜。如果第 2 号没来直接跳到第 3 号,你拒绝接收第 3 号,让厨房从第 2 号重新做。
GBN 的优缺点#
优点:简单。接收方不需要缓存乱序包。
缺点:浪费严重。如果窗口大小是 100,第 2 个包丢了,后面 98 个包虽然可能已经到达,但全部要重传。在丢包率高的网络中性能急剧下降。
3.3 选择重传(Selective Repeat, SR)#
核心规则#
SR 是 GBN 的优化版——只重传出错的包,不再”株连九族”。
关键设计:
- 发送方和接收方各自维护一个窗口
- 窗口内的每个包都有独立的定时器
- 接收方可以缓存乱序到达的包
发送方行为#
收到上层新数据:
- 窗口未满 → 发送数据,启动该包的定时器
收到 ACK n:
- 标记包 n 为已确认
- 如果 n == base → 窗口向前滑动到第一个未确认的包
- 停止包 n 的定时器
某个包超时:只重传那一个包,重启该包定时器
接收方行为#
- 收到期望范围内的包(无论是否按序)→ 缓存,发 ACK
- 收到窗口外的包 → 发 ACK(确认之前收到过)
- 当一组连续的包都收到后,一起交给上层,窗口滑动
🎯 生活类比:你网购了 10 件商品。第 2 件丢了,但第 3~10 件正常收到,你先放着。你只让卖家补发第 2 件,收到后你按顺序把所有东西整理好。
SR 的窗口大小约束#
SR 有一个重要的窗口大小约束:
窗口大小 ≤ 序号空间大小 / 2
例如如果序号用 4 位(0~15),窗口大小最大为 8。否则会出现序号歧义——接收方无法区分一个包是新的还是重传的。
3.4 GBN vs SR 对比#
| 特性 | 回退 N 步(GBN) | 选择重传(SR) |
|---|---|---|
| 累积确认 | ✅ | ❌(独立确认) |
| 接收方缓存 | ❌ | ✅ |
| 定时器 | 1 个 | N 个(每包一个) |
| 丢包后重传 | base 之后全部 | 只重传丢失的 |
| 实现复杂度 | 简单 | 较复杂 |
| 高丢包率性能 | 差 | 好 |
💡 TCP 的做法:TCP 的可靠传输机制融合了 GBN 和 SR 的思想——使用累积确认(像 GBN),但也支持选择性确认 SACK(像 SR)。这是个”取其精华”的混合方案,Day 8 会详细讲。
四、实战思考:frp UDP 心跳与可靠传输#
你在实际项目中用 frp 做内网穿透,frp 的心跳机制就是一个简单的”应用层可靠传输”实现:
1 | frpc(客户端) → frps(服务端) |
这其实就是 rdt 3.0 的超时重传思想的应用:
- 定期发送心跳包(数据包)
- 等待回复(ACK)
- 连续超时(超时重传阈值)→ 判定连接失效 → 重连
虽然简单,但原理和今天学的可靠数据传输是相通的。
五、关键知识点回顾#
UDP#
| 知识点 | 要点 |
|---|---|
| UDP 首部 | 8 字节:源端口 + 目的端口 + 长度 + 校验和 |
| 校验和 | 反码求和,只能检错不能纠错 |
| 无连接 | 不需要握手,低延迟 |
| 适用场景 | DNS、流媒体、游戏、QUIC |
| 可靠性补救 | 应用层自己实现(如 QUIC) |
可靠数据传输#
| 模型 | 信道假设 | 关键机制 |
|---|---|---|
| rdt 1.0 | 完全可靠 | 无需额外机制 |
| rdt 2.0 | 有比特错误 | ACK/NAK + 重传 |
| rdt 2.1 | 有比特错误(含ACK出错) | 增加序号(0/1) |
| rdt 2.2 | 有比特错误 | 用重复 ACK 代替 NAK |
| rdt 3.0 | 丢包 + 比特错误 | 超时重传(停等协议) |
流水线协议#
| 协议 | 策略 | 确认方式 | 接收方缓存 |
|---|---|---|---|
| GBN | base 之后全部重传 | 累积确认 | 不需要 |
| SR | 只重传出错包 | 独立确认 | 需要 |
| TCP | 混合方案 | 累积确认 + SACK | 是 |
Day 8 预告#
今天我们从 UDP 出发,一路走到了可靠数据传输的理论模型。但 rdt 只是理论模型——真实的互联网上,最广泛使用的可靠传输协议是 TCP。
Day 8 我们将深入:
- 🔸 TCP 首部详解:20 字节里藏了多少玄机?
- 🔸 TCP 连接管理:三次握手、四次挥手,为什么不是两次?
- 🔸 TCP 流量控制:发送方怎么知道接收方”吃不下”了?
- 🔸 TCP 拥塞控制:网络堵了怎么办?慢启动、拥塞避免、快速重传、快速恢复
TCP 是 rdt 理论的集大成者,也是在真实网络环境中最优雅的实用协议。明天见!
📖 读书进度:第 3 章 3.3(UDP)+ 3.4(可靠数据传输原理)
🎯 推荐实验:用 Wireshark 抓一个 DNS 查询,看看 UDP 数据报的结构;再抓一个 TCP 流,感受一下流水线传输。
下篇见 👋
计算机网络:自顶向下方法 - Day 6 | 传输层概述与多路复用
📖 Day 6 / 15 · 《计算机网络:自顶向下方法》阅读笔记
每天 30 分钟,15 天读懂计算机网络。前面五天我们都在应用层打转——HTTP、DNS、Socket——今天终于要往下走一层了。欢迎来到传输层,这是理解一切网络通信的钥匙。