网络基础(2)IP和MAC的区别与报文封装

一、局域网通信的三种标准

局域网(LAN)的通信标准,历史上留下来并被实际使用的常见有三种:

标准 一句话 现状
以太网(Ethernet) 基于碰撞检测 + 碰撞避免 ★ 最常见,90% 的局域网是以太网,原理也最复杂
无线 LAN(Wireless LAN) 原理与以太网相同,只是用无线技术承载 手机连上家里的路由器就是它
令牌环网(Token Ring) 持有令牌者才能发言 现实中很少见,但原理一句话就懂

💡 一个实用的判断顺序:先把以太网彻底搞懂,另外两种是它的变体。

  • 无线 LAN = 以太网换了个物理介质(无线电代替网线),碰撞检测与避免的逻辑一致
  • 令牌环网 = 把「先干再说,冲突了重发」改成了「先拿令牌,拿到才发」

二、★ MAC 地址:网卡的身份证

局域网里每台主机都要有唯一的标识 ,否则无法判定「这条消息是不是发给本机的」。这个标识就是 MAC 地址。

2.1 硬指标

属性 值
长度 48 比特 = 6 个字节
表示 6 组十六进制数,中间用冒号隔开(如 00:16:3e:1a:2b:3c)
存放位置 网卡内部专门的一段存储区域,出厂时写死
能否修改 硬件上不能改;但操作系统 / 虚拟机可以虚拟出一个 MAC
唯一性 全球范围内通常唯一;但真正需要的只是在同一个子网内唯一
所属层 数据链路层

2.2 怎么看

bash 复制代码
# Linux
$ ifconfig
eth0: flags=...  mtu 1500
        ether 00:16:3e:1a:2b:3c        # ← ether 后面就是 MAC

eth0 里的 eth 就是 ether(以太) 的缩写。

powershell 复制代码
# Windows
> ipconfig /all
以太网适配器 以太网:
   物理地址. . . . . . . . . . . . . : 00-16-3E-1A-2B-3C   # ← 物理地址 = MAC

✔ 两种系统显示的分隔符不同(Linux 用冒号,Windows 用连字符),这只是显示方式的问题,值是一样的。

2.3 三个容易忽略的点

① 很多硬件都有唯一标识,但 MAC 特殊在「要参与通信」

内存、磁盘也都有唯一标识(磁盘上的叫序列号 ),但那些标识只用于资产管理,不参与数据传输。网卡的 MAC 地址是要被写进帧里、参与寻址的,所以它是协议的一部分。

② 虚拟机里的 MAC 是「虚拟」出来的

在一台 Windows 上装了 Linux 虚拟机,两个系统看到的 MAC 可能不一样 ------ 因为虚拟机的网卡本身就是软件模拟出来的。这不影响通信,因为它只需要在这个虚拟网络里唯一。

③ MAC 由网卡驱动读上来

网卡里本来就有这个值,不加载操作系统它也在 。操作系统是通过网卡驱动程序把它读进内存的。


三、"以太网"这个名字,是在内涵物理学家

这个名字取得很随意,但背后的故事值得记 ------ 它同时解释了「ether」这个词和计算机工程师的幽默感。

3.1 物理学那一边

复制代码
19 世纪末的物理共识:
   声音传播需要介质
   声带振动 → 空气振动 → 耳膜振动 → 听到声音
   真空里面对面喊,听不见

        ↓  于是一个自然的推论

   光的传播,是不是也需要介质?

        ↓  继续推

   太阳到地球,光要跑 8 分钟
   可宇宙是真空的
   那宇宙里一定充满了一种看不见的介质

        ↓  给它起个名字

   ★「以太」(Ether)

当时的物理学家还在激烈争论光的本质(波动说、粒子说,后来才有波粒二象性),「以太」就是为了解释光如何传播而提出的假说。

3.2 然后它被证伪了

后来的实验(迈克耳逊-莫雷实验)反复验证,宇宙中根本不存在「以太」这种物质。

于是,「以太」在物理学界成了一个笑话------被实验证明不存在的东西,就是不存在。

3.3 计算机这边接过了这个词

几十年后,计算机工程师在实验室里搞出了一种局域网通信标准,能让两台主机互相通信了。起名字的时候:

★ 这个名字的含义是:物理学界没能证实的东西,计算机领域实实在在做出来了。

于是这种局域网标准就叫做 以太网(Ethernet)。

✔ 这不是技术内容,但记住它有两个好处:一是 eth0 的 eth 从此有了含义;二是它很典型地说明了计算机领域的命名经常带着工程师的玩笑。


四、★ 以太网通信原理:一间教室里的那堂课

这是整讲最核心的一个比喻,一定要吃透。

4.1 场景

一间教室里,一个人站在讲台前,下面坐着张三、李四、王五、赵六。

复制代码
讲台:"张三,你站起来,你的作业为什么没做?"

   李四、王五、赵六  →  听到了吗? 听到了
                    →  站起来了吗? 没有
                    →  为什么?
                       因为提取了这句话的信息,发现叫的不是自己
                       ★ 于是自动把这个消息丢弃了

   张三              →  听到了吗? 听到了
                    →  站起来了吗? 站起来了
                    →  为什么?
                       因为这句话里明确叫了「张三」

反向也一样:

复制代码
张三:"大哥,我作业早做了,是你自己没看。"

   李四、王五、赵六  →  也都听到了
                    →  但会处理这句话吗? 绝对不会
                    →  (现实中不可能李四跳出来说"你怎么能这么跟大哥说话")
                    →  ★ 因为这句话指的是「大哥」,不是自己

4.2 由此得到的结论

局域网通信的本质:消息发出去,所有人都能收到;只有目标主机会受理,其余主机全部丢弃。

现象 本质
A 认为自己在和 C 「一对一通信」 只是逻辑上的一对一
实际上 周围有一大批主机都收到了这帧,只是都丢弃了
每台主机都要有唯一名字 否则无法判定「叫的是不是本机」

✔ 这个「逻辑上一对一、物理上一对多」的反差,是理解后面所有内容(抓包、广播、碰撞)的前提。

4.3 报文长什么样

于是主机 A 要发给主机 E 的消息,至少要有三部分:

复制代码
┌──────────┬──────────┬────────────┐
│  源 MAC   │ 目的 MAC  │  消息主体   │
│  MacA    │  MacE    │  「你好」   │
└──────────┴──────────┴────────────┘

用上一讲的结论来看,这又是一个结构体:

c 复制代码
struct mac_frame {
    unsigned char src[6];    // 源 MAC,6 字节
    unsigned char dst[6];    // 目的 MAC,6 字节
    char payload[];          // 消息主体
};

💡 注意这里 unsigned char src[6] ------ MAC 的 6 个字节正好对应一个长度为 6 的字符数组。报文的每一个字段,都能在结构体里找到对应。


五、泛洪 ≠ 广播:一字之差,性质完全不同

这是一个很容易被混用的概念,课堂专门做了区分。

概念 发生了什么 结果
泛洪 消息发到局域网上,所有主机都收到了 但非目标主机丢弃(不处理)
广播 把目的 MAC 设为全 F (FF:FF:FF:FF:FF:FF) 所有主机都受理

✔ 一句话区分:泛洪是"大家都能听见",广播是"大家都要处理"。

真正的广播要靠一个特殊地址来触发------目的 MAC 全 F 是一个约定好的特殊值,网卡看到这个值就知道「这帧是给所有人的,不能丢」。


六、抓包为什么能抓到别人的包

顺着第四节的结论往下推,会得到一个略带警示性的推论:

复制代码
局域网中,所有主机都能收到所有帧
        ↓
★ 局域网里的数据是「透明」的、公开的
        ↓
所以抓包工具能抓到别人的包
        ↓
★ 这就是为什么需要加密

✔ 抓包工具的合法性,就建立在这个「所有人都能听见」的物理事实上。它不是破解了什么,而是本来就能听见。

💡 顺带解释了为什么早年很多协议(HTTP、Telnet、FTP)都不安全------它们假设了「只有通信双方能看到数据」,而这个假设在局域网里根本不成立。


七、★ 碰撞域:以太网本质上是一个临界资源

这一节会把网络课和系统课直接焊在一起。

7.1 从教室到网络

教室里能两个人同时说话吗?不能。

复制代码
正在叫张三回答作业
        ↓
李四同时大声问王五"你那五毛钱什么时候还"
        ↓
两句话混在一起,谁也听不清

换成网络:

复制代码
主机 A 正在往局域网上发消息(一串 0/1 的波形 / 高低电平)
        ↓
主机 B 同时也往局域网上发
        ↓
★ 两组光电信号在物理线路上叠加,互相干扰
        ↓
数据全废

7.2 结论

以太网中,任何一个时刻只允许一台主机向网络发送数据。

由此定义:

★ 一个以太网 = 一个碰撞域(Collision Domain)。

7.3 ★ 与操作系统的篇章相对应

把「任何时刻只允许一台主机使用以太网」这句话,和系统课的定义对齐:

系统课 网络课
临界资源 以太网
执行流(线程) 一台台主机
互斥 任何时刻只允许一台主机发
加锁 碰撞检测 + 碰撞避免

✔ 结论:以太网本质上就是一个临界资源,所有主机就是一个个执行流,在竞争这份共享资源。

💡 但这里的「保护方式」和互斥锁有个关键差别,值得单独记:

互斥锁 以太网
策略 先申请,拿到了再进 不管三七二十一先干了
冲突时 根本不会发生冲突 冲突了再说,然后重发
代价 每次都要付出申请开销 大概率一次就成功,省掉协调成本

✔ 换句话说:锁是悲观策略,以太网是乐观策略。 冲突概率低的时候,乐观策略的平均效率更高。


八、碰撞检测与碰撞避免

既然没有「锁」,那以太网怎么保证秩序?靠两步:先检测,再避免。

8.1 碰撞检测:自己发的消息自己也能收到

复制代码
A 把数据发出去
        ↓
★ A 自己也能收到自己发的这帧(类比:说话时自己能听见自己的声音)
        ↓
收回来之后做校验(CRC 等)
        ↓
发现对不上 → 判定发生了碰撞

✔ 「自己能听见自己说话」这个事实,正是碰撞检测得以实现的前提------发送方不需要别人告诉它出事了,它自己就能发现。

8.2 碰撞避免:随机休眠,错开重发

一旦检测到碰撞:

复制代码
A 检测到冲突  →  随机休眠一段时间(比如 0.1 ms)
B 也检测到冲突 →  随机休眠一段时间(比如 0.2 ms)
        ↓
两人休眠时长不同 → 时间被错开
        ↓
休眠期间,C、D、E 没人跟它们抢,可以正常竞争网络资源
        ↓
A 醒来重发 / B 醒来重发,此时冲突概率已经很低

★ 关键在**「随机」**二字:如果两台主机休眠同样长的时间,醒来后还会再撞一次,然后无限循环。


九、令牌环网:令牌就是一把锁

以太网是「先干再说」,令牌环网是另一种思路。

复制代码
教室里放一个瓶子(令牌)
        ↓
规定:只有持有瓶子的人才能说话,其余所有人闭嘴
        ↓
讲台拿着瓶子 → 说完 → 把瓶子交给张三
        ↓
张三拿着瓶子 → 只有他能说话,其他人都不能
        ↓
说完 → 传给李四 → 依次下去

★ 一句话点破:这个「令牌」,本质上就是一把锁。

系统课 令牌环网
拿到锁 拿到令牌
持有锁期间,其他线程阻塞 持有令牌期间,其他主机闭嘴
释放锁 把令牌传给下一台主机

✔ 令牌环网现实中很少遇到,但它是理解「互斥思想在不同场景下的不同实现」的绝佳对照:以太网是无锁的乐观并发,令牌环网是严格的持锁访问。


十、★ 换个视角:两台主机通信,本质是两个协议栈在通信

从这一节开始,看问题的方式要升级。

10.1 不要再把主机看成一台机器

复制代码
以前的视角:              应该升级成的视角:

   主机 A  ←───→  主机 B         协议栈 A  ←───→  协议栈 B
                                  ┌────────┐      ┌────────┐
                                  │ 应用层  │      │ 应用层  │
                                  │ 传输层  │      │ 传输层  │
                                  │ 网络层  │      │ 网络层  │
                                  │ 链路层  │      │ 链路层  │
                                  └────────┘      └────────┘

10.2 数据是谁发起的

绝大多数情况下,发起通信的是用户,也就是最上面那一层的应用。

于是完整路径是:

复制代码
发送端:用户层发起
          ↓  自顶向下贯穿协议栈(应用层 → 传输层 → 网络层 → 链路层)
       网卡
          ↓
       网络
          ↓
       对端网卡(硬件先收到)
          ↓  自顶向上交付(链路层 → 网络层 → 传输层 → 应用层)
接收端:到达对端应用层

✔ 由此得到一个重要认识:平时说的「数据链路层之间的通信」,指的只是双方链路层这一层的对话;真正的通信,是贯穿整个协议栈的。

⚠ 谈局域网时容易一叶障目:只盯着链路层那一小块,忘了上面还有三层。


十一、★ 为什么要封装:因为发数据的是用户,发得出去的是网卡

11.1 同层都要有协议

既然网络协议栈是分层的,那么每一层都要有自己的协议:

  • 应用层和之间有应用层的协议
  • 传输层和之间有传输层的协议
  • 网络层、数据链路层同理

生活中的例子------打电话:

复制代码
两个人打电话,接通后第一句都是"喂"
        ↓
没人教过,双方都会说
        ↓
★ 这是【应用层】的协议:互相确认「对端能听见本端说话了」

而两部电话之间,要互相发信号、建立连接、让对方响铃
        ↓
★ 这是【电话这一层】的协议

11.2 既然有协议,就得把协议信息带上

协议是结构体。要告诉对端「本端是谁、发给谁、数据有多长」,就必须把这些字段一起发出去。

于是:把本层的协议结构体,二进制拷贝到报文的最前面。

★ 拷贝在前面的这部分,就叫做「协议报头」。

的东西本身 | 有效载荷(payload) |

| 快递单 | 协议报头(header) |

| 盒子 + 快递单 + 商品 | 报文(报头 + 有效载荷) |

✔ 这个比喻还顺带解释了一个关键性质:报头上的字段必须写在固定位置(快递单上「收件人」写在固定的那一栏),否则对端解析不到------这一点在第六节讲抓包时还会再遇到。


十二、★ 封装的完整过程

12.1 逐层添加报头

复制代码
应用层:    「你好」
              ↓  添加应用层报头
            ┌──────────┬────────┐
            │ 应用层报头 │  你好   │
            └──────────┴────────┘
              ↓  向下交付到传输层

传输层:    ┌──────────┬──────────┬────────┐
            │ 传输层报头 │ 应用层报头 │  你好   │
            └──────────┴──────────┴────────┘
              ↑ 对传输层来说,后面整块都是「有效载荷」,
                它完全不关心里面是什么
              ↓  继续向下

网络层:    ┌────┬────┬────┬────┐
            │网络层│传输层│应用层│      │
            │ 报头 │ 报头 │ 报头 │ 你好  │
            └────┴────┴────┴────┘
              ↓  继续向下

数据链路层:┌────┬────┬────┬────┬────┐
            │链路层│网络层│传输层│应用层│      │
            │ 报头 │ 报头 │ 报头 │ 报头 │ 你好  │
            └────┴────┴────┴────┴────┘
              ↓
        ★ 这一整块才真正被网卡发到网络上

12.2 定义

★ 报文从协议栈自顶向下逐次交付、不断添加报头的过程,叫做「封装」。

概念 含义
封装 自顶向下,逐层添加本层报头
报头(header) 本层的协议结构体,加在报文最前面
有效载荷(payload) 上层交付下来的整块内容,本层不解释它

💡 注意「有效载荷」这个词是相对的:

  • 对传输层来说,有效载荷 = 应用层报头 + 数据
  • 对链路层来说,有效载荷 = 网络层报头 + 传输层报头 + 应用层报头 + 数据

每一层都把上层交下来的整块当成不透明的载荷,只在最前面加自己的那一段。

12.3 为什么要自顶向下

原因只有一句:想发数据的是用户(在最上层),能把数据发出去的是网卡(在最下层)。

复制代码
用户在应用层敲下"发送"
        ↓
但网卡是硬件,操作系统是网卡的管理者
        ↓
★ 所以数据必须穿过操作系统,也就必须穿过整个协议栈

和系统课对齐:

复制代码
printf("hello");
        ↓
printf 底层封装了系统调用 write()
        ↓
因为操作系统是显示器的管理者,用户不能绕过它

send(sockfd, buf, len, 0);
        ↓
同样是系统调用
        ↓
因为操作系统是网卡的管理者,用户不能绕过它

✔ 「网络是操作系统的一部分」这个上一讲的结论,在这里有了具体的落点:发一个网络数据,本质上就是几次系统调用。

12.4 报头里到底写了什么

以应用层为例,最简单的报头只需要一个字段:

c 复制代码
struct app_header {
    int len;        // 标明后面的数据有多少字节
};

对端收到后,直接读报文最前面的 sizeof(int) 个字节,就知道后面还有多长------这就是「快递单上写明件数」的网络版本。

后续课程里会遇到的真实报头,也都是结构体:

报头 结构体 层次
IP 报头 struct iphdr(含源 IP、目的 IP 等字段) 网络层
TCP 报头 struct tcphdr(含端口号、标志位等) 传输层
UDP 报头 struct udphdr 传输层

✔ 无论哪一层,报头 = 结构体变量,这一点是上一讲「协议就是结构体」的直接延续。

12.5 一个类比:把对象写进文件再读出来

写 C 语言通讯录时,把 struct person 数组一个个写进文件,之后再按同样的结构体从文件里读回来,数据就恢复了。

复制代码
同一个结构体 + 同样的读写顺序 → 数据可以原样还原
        ↓
★ 而 Linux 下,网络也是文件
        ↓
所以「往文件里写」和「往网络里写」在这件事上没有区别

✔ 这解释了封装为什么可行:双方用同一个结构体定义、按同样的顺序读写,那么前面那几个字节就一定会被解释成本层的报头字段。


十三、★ 解包与分用:每一层协议必备的两种能力

封装是自上而下加报头,那对端收到之后怎么办?反过来看。

13.1 解包:先把自己的报头摘下来

同层协议是互相认识的------对端的数据链路层收到整帧后,一定能提取出链路层报头,因为:

复制代码
两边定义的是同一个结构体
        ↓
指针指向帧的头部,强转成对应的结构体类型
        ↓
★ 头部就拿到了

把本层的报头和有效载荷分离的过程,叫做「解包」。

13.2 分用:然后交给上层的哪一个协议

解包之后有个绕不开的问题:上层不止一个协议。

层 该层有哪些协议
传输层 TCP、UDP
网络层 IP、ICMP、IGMP
数据链路层(上层) ARP、RARP

那么链路层解包之后,有效载荷该交给网络层的哪一个?网络层解包之后,又该交给传输层的 TCP 还是 UDP?

★ 因此,任何一层协议(应用层除外)的报头里,必须提供两种能力:

# 必备能力 为什么
① 能把报头和有效载荷分离 否则拆不开,也就无法向上交付
② 能表明"有效载荷该交给上层的哪个协议" 否则上层有多个协议,无从选择

辅导员的类比:

复制代码
辅导员:"张三,你把这个文件袋去交给五号宿舍楼五楼的李四老师。"
        ↓
文件袋上必须写清楚交给谁
        ↓
★ 同理:每一层在报头里都要写入
   "本层的有效载荷到对端后,应该交给上层的哪个协议"

★ 根据报头属性,决定把有效载荷交付给上层哪一个协议的过程,叫做「分用」。

13.3 完整流程

复制代码
目标主机网卡收到帧
        ↓
链路层:解包(摘掉链路层报头)→ 分用(按报头交给网络层)
        ↓
网络层:解包(摘掉网络层报头)→ 分用(按报头交给传输层)
        ↓
传输层:解包 → 分用(交给应用层)
        ↓
应用层:拿到「你好」

✔ 于是从结果上看:同层之间在逻辑上直接拿到了对方同层要发的数据,这就是「同层之间直接通信」的真正含义。

13.4 ⚠ 一个容易漏掉的细节:不是发给本机的,在链路层就被丢了

链路层报头里写着 src MAC 和 dst MAC。

复制代码
主机 B 收到一帧
        ↓
分析链路层报头,发现 dst MAC 是 MacC,不是自己
        ↓
★ 在链路层直接丢弃,不再向上交付
        ↓
上层完全感知不到这个报文曾经来过

✔ 所以站在上层的视角:只有发给自己的数据才会出现------它甚至不知道网上还有别的流量。


十四、★ 为什么叫"协议栈":封装是入栈,解包是出栈

14.1 把图换个画法就明白了

复制代码
发送端                                 接收端
                    ┌───────────┐
   「你好」          │  你好      │  ← 栈底
        ↓ 加应用层报头├───────────┤
                    │ 应用层报头  │
        ↓ 加传输层报头├───────────┤
                    │ 传输层报头  │
        ↓ 加网络层报头├───────────┤
                    │ 网络层报头  │
        ↓ 加链路层报头├───────────┤
                    │ 链路层报头  │  ← 栈顶(最外层)
                    └───────────┘

★ 教材画分层结构时,从来不解释这个过程叫什么。但把它画成栈,答案就摆在眼前:

复制代码
入栈(封装):栈空 → 1 个 → 2 个 → 3 个 → 4 个 → 5 个
出栈(解包):5 个 → 4 个 → 3 个 → 2 个 → 1 个 → 空

这就是一个栈,所以它叫「协议栈」。

14.2 各层的数据叫什么(术语统一)

层 封装出来的整个东西叫什么
应用层 请求 / 应答
传输层 ★ 数据段(segment)
网络层 ★ 数据报(datagram)
数据链路层 ★ 数据帧(frame)

✔ 所以前面反复说的"局域网通信中主机 A 发给主机 E 的消息",准确的说法是------数据帧。

14.3 任何一层只认识自己的报头

类比快递员:只关心自己派发的快递,不关心快递里装的是什么。

报文 = 报头 + 有效载荷

这个公式对每一层都成立,只是"报头"和"有效载荷"的具体内容逐层不同。


十五、网卡的混杂模式:抓包的技术实现

第六节从"局域网数据透明"的角度解释过为什么能抓包。这里补上技术细节。

网卡和驱动程序默认只会把发送给本主机的报文向上交付------发现目的 MAC 不是自己,就在链路层丢弃(第 13.4 节)。

但网卡有一种模式,叫 混杂模式(Promiscuous Mode):

混杂模式下:

收到数据帧 → 不判断目的 MAC 是不是自己

→ 无脑向上交付

→ ★ 不管是不是发给本机的,全都交给上层

复制代码
> ★ 抓包工具的原理,就是把自己的网卡设置成混杂模式。

✔ 这也说明:抓包不是"破解"了什么,只是让网卡不再帮忙过滤而已。 数据本来就在那里。

(顺带一句:这就是为什么局域网里跑明文协议很危险,加密是必需的。)

192.168.1.1

│ │ │ └─ 第 4 段

│ │ └──── 第 3 段

│ └─────── 第 2 段

└──────────── 第 1 段

复制代码
* 每一段的取值范围是 **0--255**,**正好一个字节**
* **内核里用 4 个字节**表示;点分十进制是给人看的字符串形式

```bash
$ ifconfig
eth0: flags=...  mtu 1500
        inet 172.18.33.122          # ← inet 就是 IP 地址(网络地址)
        ether 00:16:3e:1a:2b:3c     # ← ether 是 MAC 地址

16.2 ★ IP 地址的作用

IP 地址用来在全球范围内标识主机的唯一性。

16.3 公网 IP 与内网 IP(提前铺一层)

⚠ 严格说,上面那句话指的是公网 IP 。真实世界里 IP 还分公网和内网(私有)两类,完整机制属于后面 NAT 与子网划分的内容。本讲先默认「IP = 公网 IP」。

不过成因可以先讲清楚:

复制代码
IPv4 只有 32 位 → 最多表示 2^32 ≈ 42 亿台主机
        ↓
全球人口已超 80 亿,且车联网、IoT 让入网设备持续暴涨
        ↓
★ 地址远远不够

方案一:IPv6------彻底解决,但推不动

  • 中国是世界上 IPv6 技术最强的国家之一;2008 年北京奥运会的场馆网络建设全部采用 IPv6
  • 每个国家办奥运会时,都会把想向世界推广的技术拿出来展示(东京奥运会展示氢能源汽车,同理)
  • 但推广极难,两个原因:
    • 技术 :IP 协议在内核里。要让 IPv6 全球铺开,等于一夜之间把全世界所有操作系统升级一遍 ------ 做不到
    • 政治:互联网脱胎于美国的阿帕网,核心技术由西方主导,不可能让某一国用新技术把整个互联网重构一遍

方案二:NAT(网络地址转换)------现实中的答案

  • 把 IP 分成两块:公网 IP(不能重复) + 内网 IP(可以重复)
  • 内网 IP 允许重复 → 变相增加了 IP 的绝对上限
  • 访问外网时,运营商在出口处把内网 IP 换成公网 IP;返回时再做内网转发

✔ 这就解释了一个日常现象:在学校看到自己的 IP 是 192.168.x.x,回到宿舍看到的还是 192.168.x.x。 普通用户接触到的几乎全是内网 IP。

而买云服务器拿到的 IP 之所以能直接访问,是因为那是公网 IP。

💡 留下一个伏笔 :既然每台机器的 IP 都是独特的,为什么同一个局域网里所有主机的 IP 前半部分都一样?------这是子网划分与子网掩码要回答的问题,后续课程会讲。


十七、★ IP 与 MAC 到底有什么区别:跟着唐僧去取经

两套地址都号称"全球唯一",那到底为什么要两套?

17.1 先回答:哪个先有

网络的发展是从局部到整体的 ------ 先有局域网,后来才有广域网。

★ 所以 MAC 地址先有,IP 地址后有。 历史自己给出了答案。

17.2 西游记类比

唐僧带着四个"宠物"一路向西,每到一个地方,国王都会问同样的问题:

地点 国王问 唐僧答 国王补充
车迟国 和尚,你从哪儿来?到哪儿去? 我从东土大唐 来,要去西天面见佛祖求取真经 那你下一站该去女儿国
女儿国 你从哪儿来、到哪儿去?上一站从哪儿来? 从东土大唐来,去西天;上一站是车迟国 往西走,紧挨着的是黑风岭
黑风岭 和尚从哪来? 出家人不打诳语,我从东土大唐来,我要去西天 (然后就被抓走了)

★ 于是唐僧身上有两套地址:

说法 对应到网络 性质
从哪儿来、到哪儿去 源 IP / 目的 IP ★ 长远目标,全程不变
上一站从哪儿来、下一站去哪儿 源 MAC / 目的 MAC ★ 当下目标,每站都变

生活里也一样:每个人身上都有两套地址------高三天天刷题是为了"考上大学"(长远目标),今天来听课是为了"学会这一节"(当下目标)。

17.3 ★ 核心结论

★ 目的 IP 的核心价值:提供路由的参考依据。

为什么唐僧在车迟国时,下一站必须去女儿国?因为他要去西天。 到了女儿国为什么必须去黑风岭?同样因为要去西天。

  • 目的 IP 在整个转发过程中永远不变(唐僧在哪儿都说自己要去西天)
  • 目的 MAC 每一站都不一样(下一站由当地情况决定)

✔ 一句话:IP 是长远目标,MAC 是当下目标。

17.4 各位国王,就是路由器

国王做的事是:听完唐僧的长远目标,再结合自己对本地路况的了解,告诉他下一站去哪。

------这正是路由器的工作。

17.5 ⚠ 修正一个常见误解

"MAC 地址全球唯一"这句话没错,但这不代表 MAC 在广域网里有效。

★ MAC 地址本质上是一种只在局域网内有效的地址。

下一节会看到它为什么每经过一个路由器就被换掉。


十八、★ 跨网段转发的完整过程

18.1 路由器是什么

属性 说明
作用 连接两个不同的子网
网卡 至少两张,左右各连一个子网
IP 每张网卡各配一个 IP,分别属于所连的子网
是不是主机 ★ 路由器也是一台主机,只是主要职责是做路由
工作层次 网络层 + 数据链路层(教材常说"工作在网络层")

💡 当代路由器已经不止工作在网络层,甚至支持到应用层 ------ 家里的路由器就有应用层(给路由器配账号密码时,数据就是发给路由器本身的)。但正常情况下路由器只用它的网络层功能;只有当报文真的是发给路由器自己时,才会向上交付到它自己的应用层。

18.2 场景(故意把两边设成不同的局域网技术)

复制代码
     主机 A              路由器                    主机 B
  192.168.2.2        ┌──────────┐             172.168.2.2
     MacA            │ MacLeft  │  MacRight       MacB
      │              │192.168.2.1│172.168.2.1      │
      └── 以太网 ──── ┤          ├─ ── 令牌环网 ─────┘
      (左子网)       └──────────┘     (右子网)
      同子网还有 192.168.2.3 / .4 / .5 ...

注意左边是以太网、右边是令牌环网 ------ 这是刻意设计的:底下两种完全不同的局域网技术,照样能通信。 这个设计在第 19 节会兑现。

18.3 ★ 第一步:主机 A 的判断

复制代码
目的 IP = 172.168.2.2
本机 IP = 192.168.2.2(本子网是 192.168.2.x)
        ↓ 比对
172.168.2.2 与本子网所有主机都不一样
        ↓
★ 主机 A 并不知道 172.168.2.2 到底在哪儿(可能相隔千里)
  但一定能确定:这个报文绝对不是发给本子网其他主机的
        ↓
★ 结论:交给路由器 ------ 因为只有路由器通向外面

唐僧类比:唐僧不知道西天具体在哪儿,但绝不可能一出门就往东走。 方向是能自己判断的。

💡 这里有个前提:**只要有网络层,就有路由功能。主机 A 同理,它知道自己的下一跳是谁。

⚠ 主机是如何拿到路由器 MAC 地址的?这是 ARP 的职责,属于后续内容。

18.4 第二步:封装(注意这里的错位)

复制代码
网络层:    SRC = 192.168.2.2(自己)      DST = 172.168.2.2(★ 最终目标)
                        ↓
数据链路层:SRC = MacA(自己)             DST = MacLeft(★ 下一跳)

✔ IP 写的是最终目标,MAC 写的是下一跳。 这就是两套地址分工的具体体现,也是第 17 节那个结论的代码级落点。

18.5 第三步:泛洪,只有路由器收下

数据帧被扔到左子网上,所有主机都收到了:

复制代码
目的 MAC = MacLeft
        ↓
192.168.2.3 / .4 / .5 ... 比对后发现不是自己 → 在链路层直接丢弃
        ↓
★ 只有路由器把这一帧收下了

18.6 第四步:解包 → 查路由 → 重新封装

复制代码
① 链路层收到帧 → 解包,脱掉源/目的 MAC → 有效载荷交给网络层
② 网络层看到目的 IP = 172.168.2.2
③ 查路由:172.168.2.2 与右网卡(172.168.2.1)在同一子网 → 目标就在右侧
④ ★ 不能从网络层"直接横着传过去",必须重新向下交付
⑤ 重新封装链路层:SRC = MacRight(右网卡)  DST = MacB(主机 B)
⑥ 从右网卡发出去(这里走的是令牌环网的驱动和帧格式)

✔ 关键动作就一句:向上交付时解过的包,向下交付时要重新封装。 路由器干的活就是"脱衣服、穿衣服"。

18.7 ★ 最终结论

★ 路由过程中,IP 地址不变,MAC 地址一直在变。

三台主机(发送方、路由器、目标主机)各自网络层收到的报文完全一样;而底层的 MAC 帧,每经过一个路由器就被整体替换一次。

💡 放大到任意跳数都成立:哪怕中间经过 100 个路由器,网络层往上看,报文从头到尾是同一份;MAC 报头则被不断地自动替换。

十九、★ 网络层的本质:给世界上所有网络提供一层虚拟化

顺着上一节往下想,会得到一个非常重要的认识。

19.1 底层是什么,已经不重要了

复制代码
左子网是以太网,右子网是令牌环网,中间还可能有无线 LAN
        ↓
但到了网络层往上,所有报文长得完全一样
        ↓
★ 网络层往上,世界上就只有一种网络 ------ IP 网络

底层差异被谁消化掉了?数据链路层 + 各自的驱动程序。 是以太网就装以太网驱动、按以太网帧封装;是令牌环网就装令牌环驱动、按令牌环帧封装。而到了 IP 层,全部统一。

✔ 一个日常印证:在手机上(无线 LAN)发一条消息,和在有线的电脑上(以太网)发一条消息,对方收到的一模一样。 底层到底是无线 LAN 还是令牌环,已经不重要了。

19.2 这就是"加一层"的威力

★ 任何一个软件问题,都可以通过增加一层软件层来解决。

这句话在系统课已经见过一次:

场景 加的那一层 屏蔽了什么
一切皆文件 struct file + f_op 函数指针 键盘、显示器、磁盘、网卡等硬件的差异
★ IP 网络 网络层(IP) 以太网、令牌环网、无线 LAN 的差异

✔ IP 是一个伟大的发明,理由就在这里:它把异构的底层网络,抽象成了一个统一的网络。

19.3 这也解释了为什么 IP 必须写在内核里

网络层和驱动程序没有关系 ------它不关心底下是以太网还是令牌环。正因如此,它必须由每一个操作系统都实现一份;于是所有操作系统内部看到的网络,都是同一个 IP 网络。

(这一句正好呼应上一讲的结论:网络是操作系统的一部分。)

19.4 一个推论:想发明自己的局域网?随便

只要发明出来之后,把 IP 关连起来、配上对应的驱动,这套协议直接就能接入互联网。

------这正是为什么当年能有那么多种局域网标准并存(以太网、令牌环网、无线 LAN......),而上层的互联网却完全不受影响。

二十、★ 那只要 IP 行不行

既然 IP 能标识唯一主机、MAC 又每跳都换,那干脆把 MAC 去掉,链路层也用 IP 地址,行不行?

20.1 技术上:能

确实能实现。把链路层帧里的"源 MAC / 目的 MAC"换成"源 IP / 下一跳 IP",收到之后照样能解析、能转发。

20.2 但为什么没这么干

答案不是"做不到",而是没有必要,代价太大:

代价 说明
全世界的网卡驱动都要改 链路层与硬件绑定,改动意味着所有网卡厂商同步更新
网络层与链路层强耦合 IP 规则一变,链路层跟着变;链路层一变,IP 也跟着变
兼容性无法保证 网络的发展是先从局部到整体,MAC 在前、IP 在后。现在要革新,那些老系统怎么办
投入产出不成正比 做了能达到的效果,不做本来就已经达到了

★ 历史的车轮一旦碾过,痕迹就再也无法被抹去。

✔ 用软件工程的术语说,这就是向前/向后兼容、向上/向下兼容 的约束。技术选型从来不只是"能不能做到",更是"值不值得动"。


二十一、★ 最终从宏观上看,报文一直在做什么

把整讲串起来:

复制代码
主机 A:自顶向下封装
        ↓
路由器 1:链路层解包 → 分用到网络层 → 查路由 → 重新封装 → 发出
        ↓
路由器 2:解包 → 分用 → 封装 → 发出
        ↓
        ......
        ↓
主机 B:自底向上解包、分用,交付到应用层

★ 每经过一个路由器,都在重复「解包 → 分用 → 封装」这一套动作。

相关推荐
H.莓飛1 小时前
【数据结构】快速排序专题
linux·服务器·c语言·数据结构·visual studio
小HANN1 小时前
(索引 + SQL 优化):《MySQL 底层探秘②:B + 树索引与 Explain 实战,根治线上慢 SQL》
linux·运维·服务器·经验分享
lisanmengmeng1 小时前
pushgateway
linux·运维·服务器·网络
zbtlink1 小时前
从 DDR4 颗粒到路由器价签:一轮存储涨价的三层传导与复算
网络·网络安全·智能路由器
随性而行3601 小时前
企业微信 Webhook 接收不到消息?网络连通性、URL 签名验签与 API 返回错误码排查指南
java·网络·人工智能·后端·微信·企业微信
Rabitebla1 小时前
【Linux底层原理】 工具篇(二):Vim 编辑器基础操作与简单配置
linux·c++·编辑器·vim
wuminyu1 小时前
JVM规范学习总结-JVM规范基本概念
java·linux·c语言·jvm·c++
进击的荆棘2 小时前
Linux系统——进程间通信之system V
linux·共享内存·system v
水饺编程13 小时前
第2章:Linux 基础,切换到超级用户
linux·c语言·汇编