📚 第一章 网络基础知识
📄 一句话概括

真实网络是一层套一层的接入:家庭 → 小区 → 城域网 → 骨干。 每一跳都改帧头,IP 头端到端不变——只有 NAT 会动它。

📝 本节和前面几节的关系

1.9 讲完了零件,1.10 把零件拼成真实的网络。 这一节的重点是「本机到底能确认哪些事实」,读不到的就明说读不到。

一、一层套一层

从家里的手机到对端服务器,大致这么几层:

层 谁的设备 你管得了吗
家里 光猫、家用路由器 管得了
接入 楼道交换机、光纤 管不了
城域 运营商的汇聚设备 管不了
骨干 国家级骨干路由 完全管不了

第一行是唯一你能改的一层: 所以「网络慢」这件事,先在家这一层排除——换根网线、换个口、重启光猫,能解决相当一部分问题。 再往上就只能报修了。 第三行与第四行是「为什么 ping 值会突然变差」的答案: 骨干上的路由抖动、运营商之间的互联带宽,都不是你能控制的。 所以「同一个网站,早上快晚上慢」往往不是网站的问题,是路径上某一段拥塞了。

二、每跳改帧头,IP 头不动

这一条是 1.9.1 的延续,但有一个例外:

字段 每跳变吗 谁改
目的 MAC 必变 路由器重写
源 MAC 必变 路由器重写
TTL 必减 1 路由器
源/目的 IP 不变 只有 NAT 会改

前三行是标准行为:帧头描述的是「这一跳」,所以每过一台路由器就重新写一遍。 TTL 减一是为了防环,减到 0 就丢弃。 第四行那个「只有 NAT 会改」是今天最常见的例外: 你家的路由器把内网地址 192.168.x.x 换成公网地址再发出去,回来的包再换回去。 所以「IP 头端到端不变」这句话在有 NAT 的网络里是假的—— 这也解释了为什么「从外部主动连内网设备」需要端口映射。 NAT 还有一个副作用:它让「端到端」这个互联网最初的设计原则失效了。 IPv6 的一大动机就是「把端到端还回来」——地址够用,就不需要 NAT 了。

三、本机能确认什么

这一条是本节最实际的部分:

想知道 本机能读到吗 办法
自己的地址 能 psutil 直接给
本网段与广播地址 能 按位算
出口源地址 能 UDP connect 不发包
默认网关 不能 psutil 没这接口

第三行那个办法值得记住:UDP 的 connect 不发送任何包,只是「记下对端、选好源地址」。 所以它不依赖对端可达—— 哪怕那个地址是空的,getsockname() 也能告诉你「我会用哪个地址出去」。 这是判断「本机有多条线路时走哪条」的最轻量办法。 第四行要诚实:psutil 没有提供默认网关的接口,得调 ipconfig 或读注册表。 所以这一步不给数——读不到就写读不到,不编一个看起来合理的地址。

四、动手 Demo:一台机器连出去,中间经过谁

读本机地址与本网段,再用「不发包」的办法确认出口源地址。 默认网关本机读不到,这一步不给数。

import socket, psutil

for name, addrs in psutil.net_if_addrs().items():
    v4 = [a for a in addrs if a.family == socket.AF_INET]
    if v4 and not v4[0].address.startswith('169.254'):
        print('%s / %s' % (name, v4[0].address))
        print('  掩码 %s' % v4[0].netmask)

s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
s.connect(('8.8.8.8', 53))
print('出口源地址:%s' % s.getsockname()[0])
s.close()
print('默认网关:psutil 读不到,需要 ipconfig')

实跑输出:

--------------------------------------------------------------------
一台机器连出去,中间经过谁
--------------------------------------------------------------------
**「网络的构成」在书上是一张图;在本机上能确认的只有两样**:**自己的地址,和出口在哪。**

  本机:WLAN/192.168.3.152,掩码 255.255.255.0(/24)
  本网段:192.168.3.0,广播地址 192.168.3.255

下面用「连一个外部地址、再看本机用了哪个源 IP」的办法确认出口:

  出口源地址:192.168.3.152

**这个办法不发任何包**(UDP 的 connect 只是记下对端、选好源地址),**所以它不依赖对端可达。**

**网关的地址本机读不到**——**psutil 没有提供默认网关的接口**,得调 `ipconfig` 或读注册表。
**所以这一步不给数。**

**按书上的模型,从本机到外网通常经过这几跳**:
**本机 → 交换机 → 家用路由器(做 NAT)→ 运营商 → 骨干 → 对端。**
**每一跳都可能改帧头,只有 IP 头端到端不变(NAT 除外)。**

191.168.3.152/255.255.255.0 就是本机的身份, 按 /24 算,本网段是 192.168.3.0、广播地址是 192.168.3.255。 出口源地址与网卡地址一样, 说明本机只有一条出口——如果有线无线都连着,这里可能给出另一个地址,那就说明走的是另一条线。 这一步没发任何包:UDP 的 connect 只是记下对端、选好源地址, 所以它不依赖 8.8.8.8 是否可达。 这是「不改网络状态就能探路」的技巧。

30 秒实验

  1. 把 connect 的目的改成 1.1.1.1,看出口源地址变不变。
  2. 开一个命令行跑 ipconfig,把默认网关与本机地址对照一下。
  3. 想一下:为什么手机连 Wi-Fi 时,运营商的公网地址和你本机地址不一样?

常见误区

  1. 以为 IP 头全程不变。 NAT 会改源地址与端口,所以「端到端」在有 NAT 的网络里是假的。
  2. 以为能读到默认网关。 psutil 没有这个接口,只能调 ipconfig 或读注册表。
  3. 以为「网络慢」都是运营商的问题。 家里那一层是唯一你能改的,先排除网线、光猫、路由器, 再往上找。
自测三问(点开对答案) 1. 跨网段传输时,哪些字段每跳都变,哪些不变? —— **源/目的 MAC 每跳重写、TTL 每跳减一;源/目的 IP 不变**, **唯一例外是 NAT 会改源地址与端口。** 2. 怎么在不发包的前提下知道本机会用哪个地址出去? —— **UDP 的 `connect` 只是记下对端、选好源地址,不发送任何包**, `getsockname()` 就能读到出口地址。 3. NAT 破坏了什么设计原则? —— **破坏了「端到端」**:内网设备不能被外部主动连上,所以需要端口映射。 IPv6 的一大动机就是把它还回来。

下一节把一次完整的互联网通信拆开看——域名解析、建连、请求响应各花多少时间。

相关笔记