Old-School Networking / 2026 ・ No.01
古法网络入门
第1讲 全景图 ── 从按下申请按钮到送达服务器,用七层来看
发布
前端篇之后,是网络。作为进入后端之前的桥梁,我们用 12 讲依次来看程序员应该知道的网络基础。
主线只有一条:追踪报销系统里“提交申请”按钮发出的一次通信,每一讲往下潜一层。
第 1 讲是全景图。把 OSI 参考模型的七层和 TCP/IP 的四层画成地图,再实际抓住一次申请,按层把它拆开来看。
申请按钮的另一头
新井前辈,前端 15 讲我毕业了!接下来终于是服务器那边了吧。
古井在那之前问你一句。第 10 讲写的 fetch('/api/expenses'),它是怎么到达服务器的?
新井呃……写了 fetch,就到了。
古井能到的时候这样就行。哪天到不了了,你能说出卡在哪儿吗?网线、Wi-Fi、域名、端口,还是服务器?
新井……问 AI。
古井就算问 AI,决定查哪里的也是你。所以这次,我们把那一次通信从头追到尾。
在前端篇第 10 讲里,申请表单已经能用 fetch 把申请发给服务器,并收到受理编号。从页面这边看,只有一行。但这一行下面,查名字、找对方设备、建立连接、把数据分成小块运送,好几项工作同时在动。本系列就按层来看这些工作。
全 12 讲路线图(当前位置:第 1 讲)
- 掌握全景 ── OSI 参考模型的七层,和实际在运行的 TCP/IP 四层。每一层有什么设备、什么协议
- 从下往上潜 ── 从第 2 讲的物理层(网线、电波),到第 10 讲的 HTTP
- 在手边用起来 ── 第 11 讲是 Docker 和云上的网络,第 12 讲是“连不上”时的排查
- 每讲都实际测量 ── 在练习用网络里记录真实的数据包,用这些数字来讲解
网络是什么
一句话说,网络就是把计算机连起来、把数据送到的机制。从新井的电脑到服务器的路线,粗略画出来是这样的。
从申请按钮到服务器的路线(粗略)
家里或公司里的网络叫局域网(LAN,Local Area Network)。局域网的出口有路由器,再往外是运营商(ISP)的网络,再连到别的网络……这张由网络组成的网络就是互联网。服务器也待在数据中心的局域网里。
途中的设备不会把数据一口气整个送过去,而是分成叫数据包(packet)的小包裹,一台传给下一台。它不像电话那样和对方之间占住一条专线,而是像邮寄那样,每个包裹写上地址,走空着的路。一条线可以让许多通信共用,没有浪费。
为了让发送方和接收方用同样的方式解读,人们约定了数据的格式和步骤,这就是协议(protocol)。HTTP、TCP、IP、Ethernet,都是协议的名字。
网络由“连接的线路”“送达的步骤”和“约定(协议)”组成。数据分成小包裹(数据包),一台设备一台设备地接力运送。
分层来想
送达之前要做的事情很多,所以网络把工作分成层来考虑。想想寄信的时候。
邮寄的比喻。内容、收件人、地址、运输的车,各是不同人的工作
信纸上写什么,是写信人的事。信封上的“财务部 收”决定在楼里交给谁。地址决定送到哪栋楼。卡车只看地址,把信运到下一个邮局,不读信的内容。道路也不知道卡车装着什么。
网络也一样,每一层各有分工。这样做有三个好处。
- 可以替换 ── 有线换成 Wi-Fi,HTTP 的写法也不变。下层换掉,上层照常运行
- 只想自己这一层 ── 写 fetch 的时候,没人会去操心网线的电压
- 能缩小故障范围 ── “域名查到了,但连不上”,那就查域名那一层以下(第 12 讲)
新井和前端的“HTML 是骨架、CSS 是衣服、JavaScript 是肌肉”很像啊。按职责分开。
古井没错。分开了,坏在哪儿就只修哪儿。古法的匠人,不会把所有东西塞进一个箱子。
OSI 参考模型的七层
分层方法里最有名的一把“尺子”,是 OSI 参考模型。国际标准化组织(ISO)从 1977 年开始制定,1984 年以 ISO 7498 发布。它把通信的工作分成七层。
OSI 参考模型的七层。越往下越接近机器,越往上越接近应用
发送时从上层往下层传,接收时从下往上传。每一层都有主要的设备和协议。整理成一张表是这样的(详细内容见各讲)。
| 层 | 主要设备 | 主要协议、标准 | 本系列 |
|---|---|---|---|
| 7 应用层 | 服务器、电脑(上的应用)、代理、七层负载均衡、WAF | HTTP、DNS、SMTP、SSH、DHCP | 第 9、10 讲 |
| 6 表示层 | (没有专用设备) | 字符编码(UTF-8)、压缩、TLS 的加密 | 第 8 讲 |
| 5 会话层 | (没有专用设备) | TLS 的会话(老的有 NetBIOS 等) | 第 8 讲 |
| 4 传输层 | 防火墙、四层负载均衡 | TCP、UDP、QUIC | 第 6、7 讲 |
| 3 网络层 | 路由器、三层交换机 | IPv4、IPv6、ICMP | 第 4、5 讲 |
| 2 数据链路层 | 交换机(二层交换机)、Wi-Fi 接入点、网卡 | Ethernet、Wi-Fi(IEEE 802.11)、ARP、VLAN | 第 3 讲 |
| 1 物理层 | 网线、光纤、连接器、中继器、集线器 | 1000BASE-T 等标准、无线电的标准 | 第 2 讲 |
数据的叫法也按层不同。第 4 层叫段(segment),第 3 层叫包(packet),第 2 层叫帧(frame),第 1 层叫比特(bit)。平时常常统称“数据包”,但讨论分层时分开叫,就不会混乱。
新井七个都要背啊……
古井从上往下念:“应、表、会、传、网、数、物”。应用、表示、会话、传输、网络、数据链路、物理。
新井听着像一句咒语。
古井背熟了就是护身符。网络出问题时,从“物”往上一层层查。
TCP/IP 的四层
不过,现在互联网上实际在运行的不是 OSI 的协议,而是 TCP/IP。TCP/IP 由 Vint Cerf 和 Bob Kahn 在 1974 年的论文中提出雏形,1983 年 1 月 1 日,当时的 ARPANET 统一切换到了 TCP/IP。1980 年代末到 1990 年代初,人们曾为 OSI 和 TCP/IP 谁会普及而争论不休(被称为“协议战争”,Protocol Wars),最后普及的是 TCP/IP。
TCP/IP 的思路在 1989 年的 RFC 1122 中整理成了四层。和 OSI 这把尺子并排放,是这样的。
OSI 是“尺子”,TCP/IP 是“实物”
主要的区别有两个。第一,相当于 OSI 第 5、6 层的工作,在 TCP/IP 里合并在应用层。加密用的 TLS、字符编码,都是应用和程序库的事。第二,OSI 的第 1、2 层在 TCP/IP 里合并成一个链路层。
尽管如此,OSI 的层号仍是现场每天都在用的词。“二层交换机”“三层交换机”“四层负载均衡”“在七层分流”里的数字,都是 OSI 的层号。本系列也按内容讲 TCP/IP,编号用 OSI 的方式来进行。
OSI 参考模型是用来称呼各层的尺子。实际在运行的是 TCP/IP 的四层。第 5、6 层几乎看不到独立的身影,藏在应用和 TLS 里。
抓住一次申请
地图有了,来看实物。我在云端的 Linux 上,用叫网络命名空间(network namespace)的机制,搭了一个三台机器的小网络(做法在第 11 讲详细讲)。
| 角色 | IP 地址 | MAC 地址 | 做什么 |
|---|---|---|---|
| 新井的电脑 | 10.0.1.10 | 02:00:00:00:01:10 | 发送申请 |
| 路由器(电脑一侧) | 10.0.1.1 | 02:00:00:00:01:01 | 转发。也负责告诉名字(DNS) |
| 路由器(服务器一侧) | 10.0.2.1 | 02:00:00:00:02:01 | 转发 |
| 服务器 | 10.0.2.20 | 02:00:00:00:02:20 | 用 Node.js 接收 /api/expenses |
服务器的名字定为 api.keihi.example(.example 是为举例说明而保留的名字)。在电脑一侧和服务器一侧都开着 tcpdump 记录,然后发送一次申请。这里用 curl 代替浏览器,发送的内容也比第 10 讲的表单小,只是一个 JSON。
# 在新井的电脑(10.0.1.10)上,用 curl 重现和申请相同的发送
$ curl -X POST -H 'Content-Type: application/json' \
--data-binary '{"title":"ニューヨーク出張","total":46851}' \
http://api.keihi.example:3000/api/expenses
{"id":"EXP-2026-0012","status":"submitted"}
# 收到了受理编号(HTTP 状态是 201 Created)
受理编号 EXP-2026-0012 回来了。这时在新井的电脑上流过的数据包,一共 14 个。
# 在新井的电脑上记录的一次申请的全部内容(编号、经过时间、发送方、接收方、种类、字节数、内容) # ① ARP:向所有人询问路由器(10.0.1.1)的 MAC 地址 1 0.000ms 02:00:00:00:01:10 -> broadcast ARP 42 Who has 10.0.1.1? Tell 10.0.1.10 2 0.012ms 02:00:00:00:01:01 -> 02:00:00:00:01:10 ARP 42 10.0.1.1 is at 02:00:00:00:01:01 # ② DNS:向路由器的 DNS 询问 api.keihi.example 的 IP 地址 3 0.014ms 10.0.1.10 -> 10.0.1.1 DNS 77 query A api.keihi.example 4 0.350ms 10.0.1.1 -> 10.0.1.10 DNS 93 response A api.keihi.example A 10.0.2.20 # ③ TCP:与服务器的 3000 端口建立连接(三次往来) 5 0.680ms 10.0.1.10 -> 10.0.2.20 TCP 74 49914 -> 3000 [SYN] Seq=0 6 0.808ms 10.0.2.20 -> 10.0.1.10 TCP 74 3000 -> 49914 [SYN, ACK] Seq=0 Ack=1 7 0.818ms 10.0.1.10 -> 10.0.2.20 TCP 66 49914 -> 3000 [ACK] Seq=1 Ack=1 # ④ HTTP:发送申请,收到受理编号 8 0.891ms 10.0.1.10 -> 10.0.2.20 HTTP 266 POST /api/expenses HTTP/1.1 + JSON 9 0.898ms 10.0.2.20 -> 10.0.1.10 TCP 66 3000 -> 49914 [ACK] Seq=1 Ack=201 10 7.011ms 10.0.2.20 -> 10.0.1.10 HTTP 269 HTTP/1.1 201 Created + JSON 11 7.033ms 10.0.1.10 -> 10.0.2.20 TCP 66 49914 -> 3000 [ACK] Seq=201 Ack=204 # ⑤ TCP:关闭连接 12 7.206ms 10.0.1.10 -> 10.0.2.20 TCP 66 49914 -> 3000 [FIN, ACK] Seq=201 Ack=204 13 8.472ms 10.0.2.20 -> 10.0.1.10 TCP 66 3000 -> 49914 [FIN, ACK] Seq=204 Ack=202 14 8.479ms 10.0.1.10 -> 10.0.2.20 TCP 66 49914 -> 3000 [ACK] Seq=202 Ack=205
14 个数据包的顺序。左边的编号就是上面记录里的 No.
在发出申请(第 8 个)之前,已经来回了 7 个。先向所有人询问路由器的 MAC 地址(ARP),再用服务器的名字查 IP 地址(DNS),然后和服务器建立连接(TCP 的三次往来),这才是正题 POST。收到受理编号后,关闭连接。每一项都是本系列某一讲的主角。
| 实测(电脑一侧) | 数值 |
|---|---|
| 数据包个数 | 14 个(ARP 2、DNS 2、TCP 连接 3、HTTP 与确认 4、断开 3) |
| 流过的字节总数 | 1,333 字节 |
| 其中正题(申请的 JSON 50 + 回复的 JSON 43) | 93 字节(约 7%) |
| 从第一个 ARP 到最后的确认 | 8.479 毫秒(同一台机器里的练习网络,所以非常快) |
新井正题才 93 字节,总共却有 1,333 字节……剩下的是什么?
古井是信封、收件人和运单。下一章,拆开一个看看。
信封里套信封
第 8 个数据包(申请本身)是 266 字节。在 Wireshark 里逐层选中,该层负责的字节就会变蓝。
从开头起,依次是 Ethernet、IP、TCP 的标头(头部,header),后面接着 HTTP 的文字和 JSON 正文。按层分开写出来是这样的。
# ① Ethernet II(14 字节)
# 目的 MAC 02:00:00:00:01:01(路由器)、源 MAC、类型 08 00(里面是 IPv4)
02 00 00 00 01 01 02 00 00 00 01 10 08 00
# ② IPv4(20 字节)
# 第 9 字节 40=TTL 64,第 10 字节 06=里面是 TCP,
# 0a 00 01 0a=源 10.0.1.10,0a 00 02 14=目的 10.0.2.20
45 00 00 fc 1b f8 40 00 40 06
06 e7 0a 00 01 0a 0a 00 02 14
# ③ TCP(32 字节)
# c2 fa=源端口 49914,0b b8=目的端口 3000,
# 后面是序号、确认号、标志等
c2 fa 0b b8 83 5b 4e 23 ab f5 ef c6 80 18 00 3f
18 0c 00 00 01 01 08 0a b7 e2 b8 5b 9f a1 af c2
# ④ HTTP(150 字节)
# 从这里开始是文字。行尾是 0d 0a(\r\n),空行表示头部结束
POST /api/expenses HTTP/1.1
Host: api.keihi.example:3000
User-Agent: curl/8.5.0
Accept: */*
Content-Type: application/json
Content-Length: 50
# ⑤ JSON(50 字节)
# 申请的内容。日文用 UTF-8,每个字 3 字节
{"title":"ニューヨーク出張","total":46851}
266 字节的构成。越外层,越是把里面整个当作“内容”来运送
发送一方从上层开始,每层把自己的头部加在前面再交给下层。HTTP 做出的 200 字节,TCP 加上 32 字节的头部成为 232 字节,IP 再加 20 字节成为 252 字节,Ethernet 加 14 字节成为 266 字节。这叫封装。接收一方正好相反,从下层开始读取并剥掉头部,把内容往上交。
每一层都只读自己的头部。IP 不读 TCP 的头部,TCP 不读 HTTP 的文字。所以各层才能替换。
封装=信封里套信封。下层把上层的数据整个当作“内容”运送,只在前面加上自己的头部。这次的申请,50 字节的正题前面,一共加了 216 字节的头部。
另外,Wireshark 里看到的 266 字节,不包括在网线上前后附加的部分(8 字节的同步信号,以及检查是否损坏的 4 字节)。这些留到第 2、3 讲再看。
越过路由器之后
同一次申请的数据包,我在路由器之前(新井的电脑一侧)和之后(服务器一侧)两处都做了记录。并排看看。
| 项目 | 电脑一侧 | 服务器一侧 |
|---|---|---|
| 目的 MAC | 02:00:00:00:01:01 | 02:00:00:00:02:20 |
| 源 MAC | 02:00:00:00:01:10 | 02:00:00:00:02:01 |
| TTL(还能转发几次) | 64 | 63 |
| IP 校验和 | 0x06e7 | 0x07e7 |
| 源 IP → 目的 IP | 10.0.1.10 → 10.0.2.20 | 相同 |
| 端口、HTTP、JSON、总长度 | 49914 → 3000,266 字节 | 相同 |
MAC 地址前后两个整个换掉了。电脑一侧是“从电脑到路由器”,服务器一侧是“从路由器到服务器”。因为 MAC 地址是到相邻设备的地址。而 IP 地址从头到尾都不变,它是最终的目的地。用邮寄来比喻,就像卡车在每个邮局换乘,信封上的地址却不改写。
TTL 是每经过一台路由器就减 1 的数。变成 0 的数据包会被丢弃,所以迷路的数据包不会永远转下去(第 5 讲的 traceroute 就利用了这个机制)。因为 TTL 变了,IP 头部的校验用数字(校验和)也重新计算了。
设备看内容看到哪一层
不同设备拆开信封的深度不同。交换机看到第 2 层的 MAC 地址,决定从哪个口送出去。路由器看到第 3 层的 IP 地址,决定下一站交给谁,然后换上新的第 2 层再送出去。读 TCP 和 HTTP 的,只有两端的电脑和服务器(也有像防火墙、负载均衡那样,特意往上层看的设备)。
新井我还以为路由器会把内容全部读一遍。
古井它只看到地址为止。所以里面是 JSON 还是视频,都能用同样的速度运送。代价是,保护内容成了两端的工作。那是第 8 讲 TLS 的话题。
备好工具
本系列每一讲都用这样的工具来确认。都能在平时的电脑上马上用。
| 想查什么 | Mac | Windows | Linux | 讲 |
|---|---|---|---|---|
| 自己的 IP、MAC 地址 | ifconfig | ipconfig /all | ip addr | 3、4 |
| 能否到达对方 | ping | ping | ping | 5 |
| 途中的路径 | traceroute | tracert | traceroute | 5 |
| 正在用的端口 | lsof -i | netstat -ano | ss -tan | 6、7 |
| 从名字查 IP 地址 | dig | nslookup | dig | 9 |
| HTTP 的往来 | curl -v | curl.exe -v | curl -v | 10 |
| 数据包本身 | Wireshark(在界面上看)、tcpdump(记录) | 每讲 | ||
Wireshark 免费,Windows、Mac、Linux 都能用(写作时的最新版是 2026 年 9 月 23 日的 4.6.9,本文的截图是 Ubuntu 的 4.2.2)。打开这次的记录文件,逐层选中,应该能看到和第七章照片一样的样子。
抓包请在自己管理的网络里进行。在公司或学校的网络中,可能会看到别人的通信,一定要先取得管理员的许可。
结 — 一页总结与下一讲
- 网络把数据分成数据包,一台设备一台设备地接力运送。解读的约定就是协议。
- 工作分成层。换掉下层,上层不变,故障位置也能缩小。
- OSI 参考模型是七层的尺子(应、表、会、传、网、数、物)。实际运行的是 TCP/IP 的四层。
- 一次申请,电脑上流过 14 个数据包(ARP、DNS、TCP、HTTP、断开),共 1,333 字节。正题是 93 字节。
- 申请的数据包 266 字节 = Ethernet 14、IP 20、TCP 32、HTTP 150、JSON 50。这就是封装。
- 越过路由器后,MAC 被换掉,IP 不变,TTL 减 1。
- 交换机看到第 2 层,路由器看到第 3 层。读 TCP 和 HTTP 的只有两端。
新井下一讲是物理层吧。网线不就是插上就完事了吗?
古井插上就能用,是因为机器一丝不苟地遵守着某人定下的约定。那 266 字节在网线上是什么模样,我们去看看。
参考
- ITU-T X.200 — Open Systems Interconnection: Basic Reference Model
- Wikipedia — OSI model
- RFC 1122 — Requirements for Internet Hosts: Communication Layers (1989)
- IEEE Milestone — Transmission Control Protocol (TCP) Enables the Internet, 1974
- RFC 2606 — Reserved Top Level DNS Names (.example)
- Wireshark — 4.6.9 and 4.4.19 released (2026-09-23)
- Wireshark — Documentation
- tcpdump & libpcap
记录来自笔者在云端 Linux(Ubuntu 24.04)上用网络命名空间搭建的练习用网络(电脑、兼做 DNS(dnsmasq)的路由器、服务器(Node.js)),于 2026 年 10 月 24 日用 tcpdump 抓取。发送用的是 curl 8.5.0,界面是 Wireshark 4.2.2,表里的数字都是从这份记录读出的实际数值。MAC 地址是为了易读而手动设定的值(以 02 开头的本地用地址)。这个练习用网络没有使用 IPv6(IPv6 在第 4 讲讨论)。登场人物均为虚构。
古法网络入门 第1讲 — 全景图 / 2026.10