13895 字
69 分钟
计算机网络学习
2026-02-26

该篇结合初学时我的笔记,以及网上的文章,辅以AI重新整理而得1 2 3 4 5 6

HTTP和HTTPS#

1. 概念#

  • HTTP HTTP(HyperText Transfer Protocol,超文本传输协议)是应用层协议,基于 TCP/IP,用于客户端和服务器之间的数据传输。它是无状态的,每次请求相互独立,服务器不保留之前的交互信息

  • HTTPS HTTPS = HTTP + TLS(旧称 SSL)。TLS 在 TCP 和 HTTP 之间提供了加密、身份认证、数据完整性三重保障,默认端口 443。解决了HTTP三个问题

问题HTTPHTTPS
窃听明文传输,可抓包看到全部内容加密,抓包只能看到密文
篡改中间人可修改内容MAC 校验,篡改可被发现
冒充无法验证服务器身份证书机制,验证服务器真实身份

2. 组成#

2.1 HTTP#

HTTP 协议本身由报文格式规范构成,运行时依赖 TCP 连接

2.1.1 URL 的组成#

在发起 HTTP 请求之前,首先要解析 URL: ![[URL组成.png]]

  • 协议:http 或 https,决定使用哪种方式连接
  • 用户名:密码:HTTP Basic 认证,较少见,明文传输不安全
  • 主机名:域名或 IP,用于 DNS 解析和 Host 头
  • 端口:HTTP 默认 80,省略时自动补全
  • 路径:服务器上的资源位置
  • 查询参数? 后面的键值对,& 分隔
  • 锚点# 后面的内容,只在浏览器本地生效,不会发送给服务器

2.1.2 请求报文#

┌─────────────────────────────────────────────┐
│ 请求行 │
│ GET /index.php?id=1 HTTP/1.1 │
├─────────────────────────────────────────────┤
│ 请求头部 │
│ Host: www.example.com │
│ User-Agent: Mozilla/5.0 │
│ Accept: text/html,application/xhtml+xml │
│ Accept-Language: zh-CN,zh;q=0.9 │
│ Accept-Encoding: gzip, deflate, br │
│ Connection: keep-alive │
│ Cookie: session=abc123; token=xyz │
│ Referer: http://www.example.com/login │
│ X-Forwarded-For: 127.0.0.1 │
├─────────────────────────────────────────────┤
│ 空行 \r\n │ ← 头部结束的标志,必须存在
├─────────────────────────────────────────────┤
│ 请求体 │
│ username=admin&password=123456 │ ← GET 请求无请求体
└─────────────────────────────────────────────┘

2.1.3 响应报文#

┌─────────────────────────────────────────────┐
│ 状态行 │
│ HTTP/1.1 200 OK │
├─────────────────────────────────────────────┤
│ 响应头部 │
│ Date: Thu, 26 Feb 2026 10:00:00 GMT │
│ Server: Apache/2.4.41 (Ubuntu) │
│ Content-Type: text/html; charset=utf-8 │
│ Content-Length: 2048 │
│ Set-Cookie: session=abc123; HttpOnly │
│ Location: /new-page │
│ Cache-Control: no-cache │
│ X-Frame-Options: DENY │
├─────────────────────────────────────────────┤
│ 空行 \r\n │
├─────────────────────────────────────────────┤
│ 响应体 │
│ <html><body>Hello World</body></html> │
└─────────────────────────────────────────────┘

ps:关于请求报文和响应报文的详细内容就不在本篇展开说了,另行整理

2.2 HTTPS#

HTTPS 不是一个单独的协议,而是 HTTP + TLS(SSL) 的组合。它的组成可以从协议栈和 TLS 内部结构两个维度理解

2.2.1 协议层次结构#

┌─────────────────────────┐
│ HTTP │ 应用层:报文格式不变
├─────────────────────────┤
│ TLS │ 安全层:加密、认证、完整性
├─────────────────────────┤
│ TCP │ 传输层:可靠连接
├─────────────────────────┤
│ IP │ 网络层:寻址路由
└─────────────────────────┘

HTTP 报文内容本身没有变化,只是在交给 TCP 之前,先由 TLS 层进行加密处理。

2.2.2 TLS 协议的内部组成#

TLS 由四个子协议组成,各司其职:

┌──────────────┬──────────────┬──────────────┬──────────────┐
│ 握手协议 │ 变更密码规范 │ 警告协议 │ 应用数据协议 │
│ Handshake │Change Cipher │ Alert │ Application │
│ Protocol │ Spec │ Protocol │ Data │
└──────────────┴──────────────┴──────────────┴──────────────┘
↑ 以上四个都运行在 ↓
┌──────────────────────────────────────────────────────────┐
│ 记录协议 Record Protocol │
│ (负责分片、压缩、加密、加 MAC,最底层) │
└──────────────────────────────────────────────────────────┘

握手协议(Handshake Protocol):最复杂的部分,负责协商密码套件、交换证书、生成会话密钥,整个过程见下方。

记录协议(Record Protocol):握手完成后,所有数据(包括 HTTP 报文)都通过记录协议处理,流程是:数据分片 → 压缩(可选)→ 加 MAC(完整性校验)→ 加密 → 加记录头,然后交给 TCP。

2.2.3 TLS 握手过程的完整组成#

以 TLS 1.2 RSA 密钥交换为例(最经典的版本):

客户端 服务器
│ │
│──① ClientHello ───────────────────────────────> │
│ ├─ 客户端支持的 TLS 版本(如 TLS 1.2) │
│ ├─ 随机数 Random_C(28字节随机 + 4字节时间戳) │
│ ├─ Session ID(用于恢复会话,首次为空) │
│ ├─ 密码套件列表 CipherSuites │
│ │ 如:TLS_RSA_WITH_AES_128_GCM_SHA256 │
│ └─ 压缩方法列表 │
│ │
│<─② ServerHello ──────────────────────────────── │
│ ├─ 选定的 TLS 版本 │
│ ├─ 随机数 Random_S │
│ ├─ Session ID │
│ └─ 选定的密码套件(从客户端列表中挑一个) │
│ │
│<─③ Certificate ──────────────────────────────── │
│ └─ 服务器证书链(含服务器公钥) │
│ │
│<─④ ServerHelloDone ──────────────────────────── │
│ └─ 服务器握手信息发送完毕的标志 │
│ │
│ [客户端验证证书合法性] │
│ │
│──⑤ ClientKeyExchange ──────────────────────────> │
│ └─ 用服务器公钥加密的 PreMasterSecret(48字节) │
│ │
│ 双方各自用 Random_C + Random_S + PreMasterSecret │
│ 通过 PRF 函数计算出相同的 MasterSecret │
│ 再由 MasterSecret 派生出: │
│ client_write_key(客户端加密密钥) │
│ server_write_key(服务端加密密钥) │
│ client_write_MAC_key(客户端 MAC 密钥) │
│ server_write_MAC_key(服务端 MAC 密钥) │
│ │
│──⑥ ChangeCipherSpec ───────────────────────────> │
│ └─ 通知:后续消息开始加密 │
│ │
│──⑦ Finished ───────────────────────────────────> │
│ └─ 加密的握手摘要(验证握手完整性) │
│ │
│<─⑧ ChangeCipherSpec ─────────────────────────── │
│<─⑨ Finished ─────────────────────────────────── │
│ │
│════════════ 加密的 HTTP 数据传输开始 ═════════════│

密码套件的组成,以 TLS_RSA_WITH_AES_128_GCM_SHA256 为例:

TLS _ RSA _ WITH _ AES_128_GCM _ SHA256
│ │ │
密钥交换算法 对称加密算法 消息认证算法
(RSA/ECDHE) (AES-128-GCM) (SHA256)

TLS 1.3 简化了握手,只保留支持前向保密的密钥交换算法(ECDHE),废弃了 RSA 密钥交换,握手从 2-RTT 缩短到 1-RTT。

2.2.4 数字证书的组成#

证书是 HTTPS 身份认证的核心,格式遵循 X.509 标准:

┌─────────────────────────────────────────┐
│ 数字证书内容 │
├─────────────────────────────────────────┤
│ 版本号:V3 │
│ 序列号:唯一标识此证书 │
│ 签名算法:sha256WithRSAEncryption │
│ 颁发者(Issuer):CA 机构名称 │
│ 有效期:Not Before / Not After │
│ 主体(Subject):证书持有者(域名) │
│ CN = www.example.com │
│ 主体公钥:服务器的 RSA/EC 公钥 │
│ 扩展字段: │
│ SAN(Subject Alt Name): │
│ DNS:www.example.com │
│ DNS:*.example.com │
│ Key Usage:Digital Signature │
├─────────────────────────────────────────┤
│ CA 对以上内容的数字签名 │
│ (用 CA 私钥对证书内容哈希值签名) │
└─────────────────────────────────────────┘

证书链验证过程:

根 CA 证书(内置在操作系统/浏览器)
│ 签发
中间 CA 证书
│ 签发
服务器证书(发送给客户端)

客户端从服务器证书开始,逐级验证签名,直到找到内置信任的根 CA,整条链都验证通过才认为合法

3. 原理#

3.1 HTTP#

  • 1. 客户端与服务器建立连接:客户端(通常是浏览器)向服务器发送连接请求。这个连接是通过TCP(传输控制协议)来实现的,一般使用的默认端口号是80。

  • 2. 客户端发送请求:客户端向服务器发送一个请求,请求包括以下信息:

    • 统一资源标识符(URL):指定请求的资源路径和参数。
    • 协议版本号:指示客户端所使用的HTTP协议版本。
    • 请求修饰符:例如GET、POST等,用于指定请求的类型和操作。
    • 客户端信息:包括浏览器类型、操作系统等。
    • 许可内容:用于验证客户端对资源的访问权限。
  • 3. 服务器接受请求并返回响应:服务器接收到请求后,会根据请求的内容进行相应的处理。服务器会返回一个响应,响应的格式包括以下信息:

    • 状态行:包含协议版本号和一个用于表示请求成功或错误的状态码。
    • 服务器信息:包括服务器类型、版本号等。
    • 实体信息:请求的资源内容。
    • 可能的内容:响应中可能还包含其他的附加内容,如Cookie等。
  • 4. 客户端与服务器关闭连接:在完成请求和响应后,客户端和服务器会断开连接,释放资源。

3.2 HTTPS#

![[HTTPS.png]] (1)客户端向服务端443端口发起HTTPS请求。该请求中携带了客户端支持的加密算法和哈希算法;

(2)服务端收到请求,选择浏览器支持的加密算法和哈希算法。

(3)服务端将算法和数字证书发送给客户端。该证书包含了用于认证目的的服务器标识,可以是向某个可靠机构申请的,也可以是自制的;

(4)客户端对服务器端的证书进行验证,这一部分是浏览器内置的TLS完成的,具体步骤如下:

  • 首先浏览器会从内置的证书中搜索,找到该证书对应的机构,如果查到了对应的机构,则取出该机构颁发的公钥;如果没有找到,此时浏览器就会提示用户该证书不是由权威机构颁发,是不可信任的。
  • 然后用机构的证书公钥解密,得到证书的内容和证书签名,内容包括网站的网址、网站的公钥、证书的有效期等。浏览器会先验证签名的合法性,签名通过后,浏览器验证证书记录的网址是否和当前网址是一致的,不一致会提示用户。如果网址一致会检查证书有效期,证书过期了也会提示用户。这些都通过认证时,浏览器就可以安全使用证书中的网站公钥了。
  • 服务器生成一个随机密码串pre_master_secret,并使用服务器的公钥对其进行加密。

(5)客户端将加密的随机密码串传送给服务端。

(6)服务器用自己的私钥解密得到随机密码串(私钥),然后通过该随机密码串把网页内容进行对称加密,并传输给浏览器。

(7)浏览器用之前生成的私钥解密算法获取网页内容。

区别#

维度HTTPHTTPS
默认端口80443
传输内容明文TLS 加密密文
身份认证证书验证服务器身份
数据完整性无保障MAC 校验
握手开销仅 TCP 3次握手TCP握手 + TLS握手(额外1~2个RTT)
抓包可见内容完整报文只能看到域名(SNI)和IP,内容加密
URLhttp://https://

TCP和UDP#

1. TCP#

1.1 TCP 的核心特性#

TCP(Transmission Control Protocol)是面向连接、可靠、字节流的协议,发送数据前必须先建立连接,保证数据有序、完整地到达。

面向连接:通信前必须三次握手建立连接,通信后四次挥手断开
可靠传输:数据不丢失、不重复、不乱序
字节流:数据是连续的字节流,没有边界(需要应用层自己处理分包)
全双工:双方可以同时发送和接收数据

1.2 TCP 报文段结构#

0 1 2 3
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
┌─────────────────────────────┬─────────────────────────────────┐
│ 源端口号(16位) │ 目的端口号(16位) │
├─────────────────────────────┴─────────────────────────────────┤
│ 序列号 Seq(32位) │
├───────────────────────────────────────────────────────────────┤
│ 确认号 Ack(32位) │
├─────────┬───┬─┬─┬─┬─┬─┬─┬─┬─────────────────────────────────┤
│数据偏移 │保留│C│E│U│A│P│R│S│F│ 窗口大小(16位) │
│(4位) │(3)│W│C│R│C│S│S│Y│I│ │
│ │ │R│E│G│K│H│T│N│N│ │
├─────────┴───┴─┴─┴─┴─┴─┴─┴─┴─┴─────────────────────────────┤
│ 校验和(16位) │
├───────────────────────────────────────────────────────────────┤
│ 紧急指针(16位) │
├───────────────────────────────────────────────────────────────┤
│ 选项(可变长度) │
├───────────────────────────────────────────────────────────────┤
│ 数据部分 │
└───────────────────────────────────────────────────────────────┘

各字段含义:

  • 源/目的端口(各 16 位):标识发送和接收进程。

  • 序列号 Seq(32 位):本报文段数据部分第一个字节在整个字节流中的编号。TCP 把数据看成字节流,每个字节都有编号,Seq 就是当前这段数据的起始编号。初始序列号(ISN)在握手时随机生成。

  • 确认号 Ack(32 位):期望收到对方下一个报文段的第一个字节编号,表示这个编号之前的数据已全部收到。例如 Ack=1001 表示 1000 及之前的字节都已收到,期望收到从 1001 开始的数据。

  • 数据偏移(4 位):TCP 首部长度,单位是 4 字节。最小值 5(即 20 字节),最大值 15(即 60 字节)。

  • 控制位(6个标志位),这是 TCP 的核心:

标志位全称作用
SYNSynchronize建立连接,握手时使用
ACKAcknowledgment确认号有效,连接建立后所有报文都置 1
FINFinish请求断开连接,挥手时使用
RSTReset强制重置连接,异常断开
PSHPush要求接收方立即将数据交给应用层
URGUrgent紧急数据,配合紧急指针使用
  • 窗口大小(16 位):接收方告知发送方自己当前接收缓冲区的剩余空间,发送方据此控制发送速度,这是流量控制的核心。

  • 校验和(16 位):覆盖 TCP 首部和数据,用于差错检测。

1.3 三次握手(建立连接)#

客户端 服务器
│ │
│────① SYN────────────────────────────────> │
│ Seq=x(随机初始序列号) │
│ SYN=1, ACK=0 │
│ [客户端进入 SYN_SENT 状态] │
│ │
│<───② SYN+ACK────────────────────────────── │
│ Seq=y(服务器随机初始序列号) │
│ Ack=x+1(期望收到 x+1) │
│ SYN=1, ACK=1 │
│ [服务器进入 SYN_RCVD 状态] │
│ │
│────③ ACK────────────────────────────────> │
│ Seq=x+1 │
│ Ack=y+1(期望收到 y+1) │
│ ACK=1 │
│ [双方进入 ESTABLISHED 状态] │
│ │
│════════════ 数据传输开始 ════════════════════│

为什么是三次而不是两次? 两次握手服务器无法确认客户端能收到自己的消息。假如只有两次:服务器发出 SYN+ACK 后就认为连接建立了,但这条消息可能丢失,客户端实际上没收到,而服务器却傻等着,浪费资源。三次握手确保了双方都能确认对方的收发能力正常

此外,三次握手还用于同步双方的初始序列号(ISN),这是可靠传输的基础。初始序列号随机生成是为了防止历史连接的数据包干扰新连接。

SYN 洪水攻击(SYN Flood):攻击者发送大量伪造源 IP 的 SYN 包,服务器回复 SYN+ACK 后等待第三次握手,大量半连接耗尽服务器资源。防御方法是 SYN Cookie(服务器不分配资源,将连接信息编码进 Seq 中,收到第三次握手再验证)。

实解图 ![[TCP三次握手.png]]

  • 第一次握手:客户端尝试连接服务器,向服务器发送 syn 包(同步序列编号Synchronize Sequence Numbers),syn=j,客户端进入 SYN_SEND 状态等待服务器确认
  • 第二次握手:服务器接收客户端syn包并确认(ack=j+1),同时向客户端发送一个 SYN包(syn=k),即 SYN+ACK 包,此时服务器进入 SYN_RECV 状态
  • 第三次握手:第三次握手:客户端收到服务器的SYN+ACK包,向服务器发送确认包ACK(ack=k+1),此包发送完毕,客户端和服务器进入ESTABLISHED状态,完成三次握手 简化图 ![[TCP简化图.png]]

1.4 四次挥手(断开连接)#

主动关闭方(客户端) 被动关闭方(服务器)
│ │
│────① FIN────────────────────────────────> │
│ Seq=u, FIN=1 │
│ [客户端进入 FIN_WAIT_1 状态] │
│ 客户端不再发送数据,但仍可接收 │
│ │
│<───② ACK────────────────────────────────── │
│ Ack=u+1 │
│ [服务器进入 CLOSE_WAIT 状态] │
│ [客户端进入 FIN_WAIT_2 状态] │
│ 服务器可能还有数据要发送... │
│ │
│<───③ FIN────────────────────────────────── │
│ Seq=v, FIN=1 │
│ [服务器进入 LAST_ACK 状态] │
│ 服务器数据发完,请求关闭 │
│ │
│────④ ACK────────────────────────────────> │
│ Ack=v+1 │
│ [客户端进入 TIME_WAIT 状态,等待 2MSL] │
│ [服务器收到后进入 CLOSED 状态] │
│ │
│ [客户端等待 2MSL 超时后进入 CLOSED 状态] │
  • 为什么是四次而不是三次?

因为 TCP 是全双工的,双方各自需要关闭自己的发送通道。服务器收到客户端的 FIN 后,可能还有数据没发完,所以先单独回一个 ACK,等数据发完再发 FIN,因此 ACK 和 FIN 分成两次发送,共四次。

  • 为什么 TIME_WAIT 要等 2MSL?

MSL(Maximum Segment Lifetime)是报文最大存活时间,通常为 60 秒。等待 2MSL 有两个原因:

第一,防止最后一个 ACK 丢失。若服务器没收到最后的 ACK,会重发 FIN,客户端在 2MSL 内能重新接收并回复。

第二,防止旧连接的数据包影响新连接。等待 2MSL 后,本次连接的所有报文都已在网络中消失,不会污染下一个使用相同四元组的连接。

1.5 TCP 可靠传输机制#

1.5.1 确认与重传#

发送方发出数据后启动重传定时器,若在超时时间内没收到 ACK,则重传该数据。接收方收到数据后发送 ACK 确认。

1.5.2 滑动窗口#

不是发一条等一个 ACK,而是可以连续发送多条,窗口内的数据都可以同时在途:

发送方视角:
已发送且已确认 | 已发送未确认 | 可以发送 | 不可发送
─────────────────────────────────────────────────>
↑ ↑
发送窗口左边界 发送窗口右边界
窗口大小 = min(拥塞窗口, 接收方通告窗口)

窗口大小决定了”在途数据”的上限,越大吞吐量越高,但也越容易导致网络拥塞。

1.5.3 流量控制#

接收方在每个 ACK 中通过窗口字段告知发送方自己的缓冲区剩余大小,发送方不能超过这个量发送,防止接收方来不及处理被淹没。

1.5.4 拥塞控制#

流量控制针对接收方,拥塞控制针对网络。TCP 维护一个拥塞窗口(cwnd),经历四个阶段:

cwnd
│ ╱
│ ╱────
│ ╱────
│ ╱────
│ ╱│ 线性增长(拥塞避免)
│ ╱ │
│╱ ssthresh 指数增长(慢启动)
└─────────────────────────> 时间
慢启动 拥塞避免 检测到拥塞
ssthresh减半,cwnd重置

慢启动:初始 cwnd=1,每收到一个 ACK,cwnd 翻倍(指数增长),直到达到慢启动阈值(ssthresh)。

拥塞避免:达到 ssthresh 后,每个 RTT 只增加 1(线性增长),更保守。

检测到丢包(超时重传):ssthresh 减半,cwnd 重置为 1,重新慢启动。

快重传+快恢复:收到 3 个重复 ACK 表示丢包,ssthresh 减半,cwnd 设为新 ssthresh,不回到慢启动,直接进入拥塞避免(比超时重传更温和)。

2. UDP#

2.1 UDP 的核心特性#

UDP(User Datagram Protocol)是无连接、不可靠、数据报的协议,发送前不需要建立连接,尽力而为地传输,不保证到达,不保证顺序,不保证不重复。

无连接:直接发送,不握手
不可靠:没有确认、重传、排序机制
数据报:有边界,一次发送就是一个完整的数据报,接收方一次收一个
低开销:首部只有 8 字节,处理速度快

2.2 UDP 报文结构#

UDP 的结构极其简单,首部固定只有 8 字节

┌─────────────────────────────┬─────────────────────────────────┐
│ 源端口号(16位) │ 目的端口号(16位) │
├─────────────────────────────┼─────────────────────────────────┤
│ 长度(16位) │ 校验和(16位) │
├─────────────────────────────┴─────────────────────────────────┤
│ 数据部分 │
└───────────────────────────────────────────────────────────────┘
字段大小说明
源端口16位可选,不需要回复时可填 0
目的端口16位必须,标识接收进程
长度16位UDP 首部+数据的总长度,最小值 8
校验和16位可选,用于差错检测,IPv4 中可置 0 跳过
对比 TCP 的 20 字节最小首部,UDP 只有 8 字节,处理开销极小。

2.3 UDP 没有的东西#

理解 UDP 最好的方式是列出它不具备的 TCP 特性,以及为什么:

TCP 特性UDP 有无影响
三次握手无连接延迟,但无法确认对方在线
确认机制发完不管,不知道对方有没有收到
重传机制丢包不重发
序号/排序多包乱序不纠正
流量控制可能淹没接收方
拥塞控制网络拥塞时仍全速发送
全双工双方都可发送

3. 区别#

维度TCPUDP
连接方式面向连接(需握手)无连接(直接发)
可靠性可靠,保证到达、有序、不重复不可靠,尽力而为
数据单位字节流(无边界)数据报(有边界)
首部大小最小 20 字节固定 8 字节
速度慢(确认、重传、拥塞控制开销)快(无额外机制)
拥塞控制
广播/多播不支持支持
适用场景要求数据完整要求低延迟

4. 各自的适用场景#

4.1 TCP 适合#

对数据完整性要求高、可以接受延迟的场景:

  • HTTP/HTTPS:网页传输,数据缺失直接显示错误
  • FTP:文件传输,文件损坏不可接受
  • SSH:远程登录,指令不能乱序或丢失
  • SMTP/POP3:邮件传输
  • 数据库连接:MySQL、PostgreSQL 等

4.2 UDP 适合#

实时性要求高、少量丢包可以接受的场景:

  • DNS:查询短小,一来一回,不需要连接开销
  • 视频直播/音视频通话:丢几帧比卡顿更能接受(WebRTC 基于 UDP)
  • 在线游戏:位置同步,旧数据丢了没关系,要的是最新状态
  • DHCP:获取 IP 地址,广播场景
  • QUIC(HTTP/3):Google 设计的基于 UDP 的传输协议,在应用层自己实现可靠性,避免 TCP 的队头阻塞

MAC地址、IP地址#

1. 为什么需要两种地址#

很多人会疑惑:既然有了 IP 地址能定位主机,为什么还需要 MAC 地址?理解这个问题,先要明白网络通信的分层逻辑。

互联网是由无数个局域网互联组成的,数据从源主机到目标主机,要经过多个网络、多个路由器的转发。整个过程分成两个层面:

源主机 ──→ 路由器A ──→ 路由器B ──→ 目标主机
LAN1 WAN LAN2
IP 地址负责:端到端的寻址(从源主机到目标主机的全局路径)
MAC 地址负责:每一跳的寻址(当前这一段链路上,数据交给谁)

打个比方:IP 地址像是”快递的目的地城市+街道”,负责规划从出发地到目的地的整体路线;MAC 地址像是”当前这段路上下一个中转站的门牌号”,每到一个中转点就换一个新的目标门牌,但最终目的地(IP)始终不变。

2. MAC地址#

2.1 概念#

MAC 地址(Media Access Control Address,媒体访问控制地址)是网络设备网卡(NIC) 的硬件地址,工作在数据链路层(第二层),用于同一局域网内的节点寻址。

每块网卡出厂时由厂商烧录一个全球唯一的 MAC 地址,理论上不会重复,所以也叫物理地址硬件地址

2.2 格式#

MAC 地址长度为 48 位(6 字节),通常用十六进制表示,每字节用 :- 分隔:

00:1A:2B:3C:4D:5E
00-1A-2B-3C-4D-5E ← Windows 风格
001A.2B3C.4D5E ← Cisco 风格

这 6 个字节分成两部分:

┌─────────────────────────┬─────────────────────────┐
│ OUI(3字节) │ 设备序列号(3字节) │
│ 组织唯一标识符 │ 厂商自行分配 │
│ 由 IEEE 分配给厂商 │ │
└─────────────────────────┴─────────────────────────┘
00:1A:2B 3C:4D:5E
这3字节可以查出是哪家厂商
00:1A:2B → 某网卡厂商

OUI 第一个字节的最低两位有特殊含义:

第一字节:XX XX XX XX XX X[G][M]
│ └── G位:0=单播,1=组播/广播
└───── M位:0=全局唯一(厂商分配),1=本地管理(手动设置)

特殊 MAC 地址:

地址含义
FF:FF:FF:FF:FF:FF广播地址,局域网内所有设备都接收
01:xx:xx:xx:xx:xx组播地址(G位为1)
00:00:00:00:00:00无效/未分配

2.3 原理#

在局域网内,数据封装成以太网帧,帧头部包含源 MAC 和目的 MAC:

┌──────────┬──────────┬──────┬──────────┬─────┐
│目的MAC(6B)│源MAC(6B) │类型 │ 数据 │FCS │
│ │ │(2B) │(46~1500B) │(4B) │
└──────────┴──────────┴──────┴──────────┴─────┘

交换机维护一张 MAC 地址表(也叫 CAM 表),记录每个 MAC 地址对应哪个端口:

MAC 地址表:
┌─────────────────────┬──────┐
│ MAC 地址 │ 端口 │
├─────────────────────┼──────┤
│ 00:1A:2B:3C:4D:5E │ 1 │
│ 00:2C:3D:4E:5F:6A │ 3 │
│ 00:3E:4F:50:61:7B │ 5 │
└─────────────────────┴──────┘

收到帧时,交换机查表找到目的 MAC 对应的端口,精确转发;若不在表中,则泛洪(向所有端口广播)。这就是为什么 MAC 地址只在局域网内有效——出了这个局域网,交换机不认识外面的 MAC。

2.4 修改#

虽然叫”物理地址”,但现代操作系统允许在软件层面修改 MAC 地址(MAC 欺骗 / MAC Spoofing):

Terminal window
# Linux 修改 MAC 地址
ip link set eth0 down
ip link set eth0 address 00:11:22:33:44:55
ip link set eth0 up
# Windows 可以在网卡属性中修改

修改的只是系统对外发送帧时使用的 MAC 值,硬件本身烧录的值不变。

CTF/安全场景:MAC 欺骗可以绕过基于 MAC 的访问控制(如某些 WiFi 的 MAC 白名单);ARP 欺骗攻击中也会伪造 MAC 地址。


3. IP地址#

3.1 概念#

IP 地址(Internet Protocol Address)是网络层给每个网络接口分配的逻辑地址,用于在跨网络通信中唯一标识一台主机的网络接口,并为路由器提供寻路依据。

注意”网络接口”而非”主机”——一台主机可以有多块网卡,每块网卡有独立的 IP 地址;一块网卡也可以绑定多个 IP 地址。

目前有两个版本并存:IPv4(32位,主流)和 IPv6(128位,逐步普及)。

3.2 IPv4 地址#

3.2.1 格式与表示#

IPv4 地址是 32 位二进制数,为了方便人类阅读,用点分十进制表示,将 32 位分成 4 组,每组 8 位转成十进制,用 . 分隔,每组范围 0~255:

二进制: 11000000 . 10101000 . 00000001 . 01100100
↓ ↓ ↓ ↓
十进制: 192 . 168 . 1 . 100

3.2.2 网络部分与主机部分#

IP 地址不是一个整体,而是由两部分组成:

┌─────────────────────────┬─────────────────────────┐
│ 网络部分 │ 主机部分 │
│ 标识主机所在的网络 │ 标识网络中的具体主机 │
└─────────────────────────┴─────────────────────────┘

网络部分相同的主机属于同一个网络,可以直接通信;网络部分不同则需要经过路由器转发。

如何区分哪些位是网络部分,哪些位是主机部分? 靠子网掩码。

3.2.3 子网掩码与 CIDR#

子网掩码也是 32 位,其中连续的 1 对应网络部分,连续的 0 对应主机部分:

IP 地址: 192.168. 1.100
11000000.10101000.00000001.01100100
子网掩码: 255.255.255. 0
11111111.11111111.11111111.00000000
←────── 网络部分(24位)────────→←主机(8位)→
IP 与子网掩码按位与 → 得到网络地址:
11000000.10101000.00000001.00000000
= 192 . 168 . 1 . 0

现代更常用 CIDR(无类域间路由)表示法,直接在 IP 后面加斜线和网络位数:

192.168.1.100/24 ← 等价于子网掩码 255.255.255.0,前24位是网络部分
10.0.0.1/8 ← 等价于子网掩码 255.0.0.0,前8位是网络部分
172.16.5.1/12 ← 等价于子网掩码 255.240.0.0,前12位是网络部分
  • 一个子网中的特殊地址192.168.1.0/24 为例,这个子网有 2⁸ = 256 个地址:
192.168.1.0 ← 网络地址(主机部分全0),标识这个网络本身,不能分配给主机
192.168.1.1
192.168.1.2
... ← 可分配给主机的地址,共254个
192.168.1.254
192.168.1.255 ← 广播地址(主机部分全1),发给它的数据包该子网所有主机都会收到

可用主机数 = 2^(主机位数) - 2,减去的是网络地址和广播地址。

  • 子网划分举例192.168.1.0/24 划分成 4 个子网,需要借用 2 位主机位做子网位,变成 /26:
192.168.1.0/26 主机范围:192.168.1.1 ~ 192.168.1.62 (62台)
192.168.1.64/26 主机范围:192.168.1.65 ~ 192.168.1.126 (62台)
192.168.1.128/26 主机范围:192.168.1.129 ~ 192.168.1.190 (62台)
192.168.1.192/26 主机范围:192.168.1.193 ~ 192.168.1.254 (62台)

子网掩码从 /24 变成 /26,网络数增加了,但每个网络里的主机数减少了,这是子网划分的核心权衡。

3.2.4 IPv4 地址#

  • 传统有类地址(A/B/C/D/E类) 历史上按照 IP 地址的开头几位来自动判断网络部分和主机部分的长度:
A 类:首位为 0
范围:1.0.0.0 ~ 126.255.255.255
默认掩码:/8,网络数少(126个),每个网络主机数极多(约1600万)
适合:超大型机构
B 类:首两位为 10
范围:128.0.0.0 ~ 191.255.255.255
默认掩码:/16,网络数适中(16384个),每个网络主机数适中(65534台)
适合:大中型机构
C 类:首三位为 110
范围:192.0.0.0 ~ 223.255.255.255
默认掩码:/24,网络数多(约200万个),每个网络主机数少(254台)
适合:小型网络
D 类:首四位为 1110
范围:224.0.0.0 ~ 239.255.255.255
用途:组播(Multicast),不分配给单个主机
E 类:首四位为 1111
范围:240.0.0.0 ~ 255.255.255.255
用途:保留/实验用,不在互联网上使用

有类地址的问题是地址浪费严重。一个 B 类地址块给一个只有 300 台主机的公司,6 万多个地址白白浪费。CIDR 的出现解决了这个问题,允许任意长度的网络前缀,不再受 ABC 类约束。

  • 特殊用途地址(重点) ×7 这些地址有特殊含义,不能或不应该用于普通主机:
  1. 回环地址:
127.0.0.0/8
最常用:127.0.0.1(localhost)

发给回环地址的数据包不会到达网络,由操作系统直接在内部处理,用于本机进程间通信和网络软件测试。整个 127.0.0.0/8 都是回环地址,127.0.0.1 只是约定俗成最常用的一个。

CTF 场景:SSRF 中常用 127.0.0.1localhost127.1(简写形式)访问服务器本地服务,常见的绕过还包括:

http://127.0.0.1/admin
http://localhost/admin
http://0/admin ← 0 有时解析为 0.0.0.0,等价于 127.0.0.1
http://[::1]/admin ← IPv6 回环地址
http://127.1/admin ← 省略中间两组,部分系统支持
http://0177.0.0.1/admin ← 八进制表示
http://0x7f.0.0.1/admin ← 十六进制表示
  1. 私有地址:
10.0.0.0/8 A类私有,范围 10.0.0.0 ~ 10.255.255.255
172.16.0.0/12 B类私有,范围 172.16.0.0 ~ 172.31.255.255
192.168.0.0/16 C类私有,范围 192.168.0.0 ~ 192.168.255.255

私有地址只在局域网内使用,路由器不会将其路由到公网,多个局域网可以使用相同的私有地址段而不冲突,访问公网时需要经过 NAT 转换成公网 IP。

CTF 场景:SSRF 中通过访问这些地址段探测内网服务,如 http://192.168.1.1/ 可能是内网路由器管理页面。

  1. 链路本地地址:
169.254.0.0/16

当主机无法通过 DHCP 获取 IP 地址时,操作系统自动分配此范围内的地址(APIPA),只在本地链路有效。在云服务器上,169.254.169.254 是元数据服务(IMDS)的固定地址,包含实例的配置信息、凭证等。

CTF/云安全场景:SSRF 访问 http://169.254.169.254/latest/meta-data/ 可以获取 AWS、阿里云等云服务器的元数据,包括 IAM 临时密钥,是云环境 SSRF 的经典利用方式。

  1. 受限广播地址:
255.255.255.255

发给这个地址的数据包被当前网络内所有主机接收,路由器不转发。主要用于 DHCP 请求(主机还没有 IP,只能广播)。

  1. 本机地址:
0.0.0.0

含义依场景而定。作为源地址时表示”本机的任意地址”(还没分配 IP 时使用,如 DHCP 请求);作为服务监听地址时表示”监听本机所有网络接口”,比 127.0.0.1 监听范围更广,外部也能访问。

  1. 文档示例地址:
192.0.2.0/24 (TEST-NET-1)
198.51.100.0/24 (TEST-NET-2)
203.0.113.0/24 (TEST-NET-3)

RFC 5737 规定的文档示例专用地址,不会在实际网络中路由,写教程、文档时用这些地址举例不会和真实地址冲突。

  1. 运营商级 NAT 地址:
100.64.0.0/10

RFC 6598 定义,供运营商在大规模 NAT 场景下使用,介于公网和私网之间。


3.2.5 IPv4 的问题:地址耗尽#

IPv4 只有 32 位,理论最大地址数约 43 亿,但扣掉各种保留、私有、广播地址后可用公网地址更少。随着互联网爆发式增长,IPv4 公网地址已于 2011 年被 IANA 分配完毕。

目前的缓解措施是 NAT(多台内网设备共享一个公网 IP),但这破坏了端到端透明性,引发了一系列问题。根本解决方案是 IPv6。

3.2.6 IPv4 地址分类#

传统分类(有类寻址,了解即可):

类别首位范围默认子网掩码用途
A 类01.0.0.0 ~ 126.255.255.255/8大型网络
B 类10128.0.0.0 ~ 191.255.255.255/16中型网络
C 类110192.0.0.0 ~ 223.255.255.255/24小型网络
D 类1110224.0.0.0 ~ 239.255.255.255组播
E 类1111240.0.0.0 ~ 255.255.255.255保留/实验

现在普遍使用 CIDR(无类域间路由),子网掩码可以是任意位数,不再受 ABC 类限制。

特殊 IP 地址(必须掌握):

地址/范围含义CTF 场景
127.0.0.1本机回环地址(localhost)SSRF 绕过 IP 限制
0.0.0.0本机所有接口 / 未指定地址服务监听所有网卡
255.255.255.255受限广播地址DHCP 请求
10.0.0.0/8私有地址 A 类内网地址
172.16.0.0/12私有地址 B 类内网地址
192.168.0.0/16私有地址 C 类家用路由器常用
169.254.0.0/16链路本地地址(APIPA)DHCP 失败时自动分配
100.64.0.0/10运营商级 NAT 地址大型 ISP 内部
私有地址不能直接路由到公网,需要通过 NAT 转换。

3.3 IPv6 地址#

3.3.1 格式与表示#

IPv6 地址是 128 位,分成 8 组,每组 16 位用十六进制表示,组间用 : 分隔:

完整写法:2001:0DB8:0000:0000:0000:FF00:0042:8329

简写规则:

第一,每组内前导零可以省略:

2001:0DB8:0000:0000:0000:FF00:0042:8329
→ 2001:DB8:0:0:0:FF00:42:8329

第二,一段或多段连续的全零组可以用 :: 替代,但整个地址中 :: 只能出现一次:

2001:DB8:0:0:0:FF00:42:8329
→ 2001:DB8::FF00:42:8329

:: 展开时,用足够多的 0000 补全到 128 位。因为只能出现一次,所以展开结果是唯一确定的。

常见特殊地址简写:

::1 ← 回环地址,等价于 IPv4 的 127.0.0.1
:: ← 未指定地址,等价于 IPv4 的 0.0.0.0

3.3.2 IPv6 地址结构#

IPv6 地址同样分网络部分和接口部分,典型的全球单播地址结构是:

┌────────────────────────┬───────────────┬────────────────────────┐
│ 全局路由前缀(48位) │ 子网ID(16位)│ 接口标识符(64位) │
│ 由 ISP/注册机构分配 │ 站点内子网划分 │ 通常由 MAC 地址生成 │
└────────────────────────┴───────────────┴────────────────────────┘

接口标识符(后 64 位)通常使用 EUI-64 方式从 MAC 地址派生:把 48 位 MAC 从中间插入 FF:FE,变成 64 位,并翻转第 7 位(U/L 位)。这意味着 IPv6 地址可能泄露设备的 MAC 地址,现代系统默认使用随机生成的临时地址来保护隐私。

3.3.3 IPv6 地址分类#

类型前缀说明
全球单播2000::/3可在全球路由的公网地址,类似 IPv4 公网 IP
链路本地fe80::/10只在本地链路有效,自动配置,不可路由
唯一本地fc00::/7类似 IPv4 私有地址,在组织内部使用
组播ff00::/8替代 IPv4 广播,发给一组设备
回环::1/128本机回环
未指定::/128未分配地址
文档示例2001:db8::/32文档教程专用,不在网络中路由
IPv6 没有广播地址,广播功能由组播替代(如 ff02::1 表示链路本地所有节点)。

IPv6 特殊地址

地址含义
::1本机回环(等价于 IPv4 的 127.0.0.1)
::未指定地址
fe80::/10链路本地地址(Link-Local),只在本段链路有效
ff00::/8组播地址
2001:db8::/32文档示例专用地址
IPv6 没有广播,用组播替代;也没有 NAT 的必要(地址空间足够大);内置 IPSec 支持。

3.4 IPv6 与 IPv4 的主要差异#

维度IPv4IPv6
地址长度32位128位
地址数量~43亿约3.4×10³⁸
表示方式点分十进制冒号十六进制
首部长度20~60字节(可变)固定40字节(扩展头单独处理)
广播无(用组播替代)
NAT普遍需要不需要(地址充足)
IPSec可选内置支持
地址配置手动或 DHCP支持无状态自动配置(SLAAC)
校验和首部有校验和首部无校验和(交给上层协议处理)
分片路由器可分片只有源主机可分片

3.5 IP 地址的分配机制#

3.5.1 静态分配#

手动配置,适合服务器、路由器等需要固定地址的设备。

3.5.2 DHCP 动态分配#

DHCP(Dynamic Host Configuration Protocol) 让主机自动获取 IP 地址,过程是四步握手:

客户端 DHCP服务器
│ │
│──① DHCP Discover─────────────────────────────>│
│ 源IP:0.0.0.0 目的IP:255.255.255.255 │
│ "我需要一个IP地址"(广播) │
│ │
│<─② DHCP Offer──────────────────────────────────│
│ "我提供给你 192.168.1.100,租期24小时" │
│ │
│──③ DHCP Request──────────────────────────────>│
│ "我接受 192.168.1.100"(广播,通知其他服务器) │
│ │
│<─④ DHCP ACK────────────────────────────────────│
│ "确认,192.168.1.100 已分配给你" │
│ 同时下发:子网掩码、网关、DNS服务器地址 │

DHCP 基于 UDP,客户端用 68 端口,服务器用 67 端口。租期到期前客户端会发送续租请求,到期未续租则收回地址。

3.6 路由与 IP 转发#

路由器根据 路由表 决定把数据包转发到哪里:

目标网络 子网掩码 下一跳 出口
192.168.1.0 /24 直连 eth0
10.0.0.0 /8 直连 eth1
172.16.0.0 /12 10.0.0.254 eth1
0.0.0.0 /0 203.0.113.1 eth2 ← 默认路由

路由器查表时使用最长前缀匹配原则:多条路由都能匹配时,选择网络前缀最长(最具体)的那条。比如目标 IP 是 192.168.1.5,同时匹配 192.168.1.0/240.0.0.0/0,选更具体的 /24

默认路由(0.0.0.0/0) 是最短前缀,匹配所有地址,在没有更具体路由时使用,相当于”不知道怎么走就发给这里”,对应普通主机配置里的”默认网关”。

4. 总结对比#

维度MAC 地址IP 地址
工作层次数据链路层(第二层)网络层(第三层)
长度48 位(6 字节)IPv4: 32位 / IPv6: 128位
表示方式十六进制,冒号分隔IPv4 点分十进制 / IPv6 冒号十六进制
分配方式出厂烧录(可软件修改)手动配置或 DHCP 动态分配
作用范围同一局域网内全球互联网
寻址目标下一跳节点最终目标主机
是否可变每一跳都换全程不变
相关协议ARP(IP→MAC 解析)DNS(域名→IP 解析)、DHCP(动态分配)
攻击手段ARP 欺骗、MAC 泛洪IP 欺骗、SSRF 内网探测

5. ARP:MAC 与 IP 的桥梁#

知道了 IP 地址,如何找到对应的 MAC 地址?这就是 ARP(Address Resolution Protocol,地址解析协议) 的工作。

5.1 ARP 工作过程#

主机A(192.168.1.1)想给主机B(192.168.1.2)发数据
但不知道 B 的 MAC 地址
① A 广播 ARP Request(目的MAC = FF:FF:FF:FF:FF:FF)
"谁是 192.168.1.2?告诉我你的 MAC 地址"
局域网内所有主机都收到这条消息
② B 发现询问的是自己,单播回复 ARP Reply
"我是 192.168.1.2,我的 MAC 是 00:BB:CC:DD:EE:FF"
③ A 收到回复,将 IP→MAC 的映射存入 ARP 缓存表
下次再给 B 发数据,直接查缓存,不再广播

ARP 缓存(查看命令):

Terminal window
# Linux/Mac
arp -a
# 输出示例:
# 192.168.1.1 at a4:5e:60:xx:xx:xx on en0
# 192.168.1.2 at 00:bb:cc:dd:ee:ff on en0

5.2 ARP 欺骗攻击#

ARP 是无状态协议,不验证回复的真实性,任何主机都可以发送伪造的 ARP Reply,这是 ARP 欺骗(ARP Spoofing) 的根源:

正常情况:
A 问:"谁是网关 192.168.1.1?"
网关回:"我是,我的MAC是 AA:AA:AA:AA:AA:AA"
ARP 欺骗:
A 问:"谁是网关 192.168.1.1?"
攻击者抢先回:"我是,我的MAC是 CC:CC:CC:CC:CC:CC"(伪造)
A 信以为真,把攻击者的 MAC 存入缓存
结果:A 发给网关的所有数据都流向了攻击者 → 中间人攻击

攻击者同时对网关也做同样的欺骗,就能双向劫持流量,实现局域网内的中间人攻击(MITM),可以嗅探、篡改流量。防御方法是静态 ARP 绑定或使用支持 DAI(Dynamic ARP Inspection)的交换机。


6. 数据转发的完整过程#

把 MAC、IP、ARP 串起来,看一个完整的跨网络通信过程:

场景:PC(192.168.1.100)访问服务器(8.8.8.8)
网络拓扑:
PC ──── 交换机 ──── 路由器(网关)──── 互联网 ──── 服务器
192.168.1.x 192.168.1.1

第一步:PC 判断目标是否在同一子网

目标 IP:8.8.8.8
本机子网:192.168.1.0/24
8.8.8.8 不在 192.168.1.0/24 中 → 需要发给网关

第二步:ARP 获取网关 MAC

PC 广播:"谁是 192.168.1.1?"
网关回复:"我是,MAC = AA:BB:CC:DD:EE:FF"
PC 缓存此映射

第三步:封装并发送以太网帧(第一跳)

以太网帧:
目的MAC = AA:BB:CC:DD:EE:FF ← 网关的 MAC
源MAC = PC的MAC
IP 数据包:
目的IP = 8.8.8.8 ← 始终不变
源IP = 192.168.1.100 ← 始终不变
数据:HTTP 请求

第四步:路由器转发(MAC 地址在每一跳都更换)

路由器收到帧,剥掉以太网帧头:
发现目的IP = 8.8.8.8
查路由表,找到下一跳出口
ARP 获取下一跳的 MAC 地址
重新封装以太网帧:
目的MAC = 下一跳路由器的MAC ← 更换了!
源MAC = 本路由器出口的MAC ← 更换了!
IP 数据包内容不变(IP 不变)

关键规律:

IP 地址(源IP、目的IP):从源头到终点全程不变
MAC 地址(源MAC、目的MAC):每经过一个路由器都更换一次

这就是两层地址分工的本质:IP 负责全局路径,MAC 负责局部链路。


7. NAT:私有 IP 访问公网的方式#

私有 IP 不能直接在公网上路由,家用路由器通过 NAT(网络地址转换) 解决这个问题:

内网多台设备 路由器(NAT) 公网服务器
192.168.1.100:54321 ──→ 公网IP:10000 ──→ 8.8.8.8:80
192.168.1.101:54322 ──→ 公网IP:10001 ──→ 8.8.8.8:80
192.168.1.102:54323 ──→ 公网IP:10002 ──→ 8.8.8.8:80

路由器维护一张 NAT 转换表,将内网的 私有IP:端口 映射到 公网IP:端口,多台内网设备共享一个公网 IP。

CTF 场景:NAT 导致从公网看到的 IP 都是路由器的公网 IP,SSRF 漏洞中常利用内网 IP 段(10.x、172.16~31.x、192.168.x)访问内网服务。

HTTP 报文的封装与解封装#

原理#

封装#

封装是在报文发送端设备进行的,报文发送时在体系结构中自高层向低层依次进行传送,每经过一层(除物理层外)都要在上层传送来的报文的最外面加装对应层所使用的协议的头部,这就是报文封装,也称“协议封装”。报文在各层经过封装后形成对应的报文单元,应用层的报文单元为“数据报”、传输层的报文单元为“数据段”、网络层为的报文单元为“数据包”,数据链路层的报文单元为“数据帧”,物理层的报文单元为“比特”。这样一来,当报文传输到最低的物理时,除了真正的数据部分外,还加装了多个协议头。

解封装#

报文封装是发送端对报文添加对应层所使用的协议的协议头,继续向下层传送,发生在体系结构中的高4层(除最低的物理层外)。报文解封装是报文封装的逆过程,是在接收端进行的,当报文要继续向上层传送时,就要去掉报文中在本层中的协议头,使得上层协议头在报文的最外面。这样做的目的是,当报文继续向上层传送时,上层就可以根据最外层协议头获取本层地址信息,然后进行相应的转发。解封装发生可能在任何一个接收端接口上发生,但是仅发生支持三层或以上层次的设备上,因为解封装也仅从数据链路层开始,直到应用层。

一、网络分层模型回顾#

在讲封装过程之前,先明确数据经过的每一层和对应的协议数据单元:

发送方 接收方
┌─────────────┐ ┌─────────────┐
│ 应用层 │ HTTP报文 │ 应用层 │
├─────────────┤ ├─────────────┤
│ 传输层 │ TCP报文段(Segment) │ 传输层 │
├─────────────┤ ├─────────────┤
│ 网络层 │ IP数据包(Packet) │ 网络层 │
├─────────────┤ ├─────────────┤
│ 数据链路层 │ 以太网帧(Frame) │ 数据链路层 │
├─────────────┤ ├─────────────┤
│ 物理层 │ 比特流(Bits) │ 物理层 │
└─────────────┘ └─────────────┘
│ ↑
└──────────── 物理介质传输 ──────────────────┘

每一层只和相邻层打交道,向下交付时加上本层头部(封装),向上交付时去掉本层头部(解封装)。每一层都只能看到本层的头部,对上层内容视为不透明的数据载荷。

二、场景设定#

用一个具体场景贯穿全程:

浏览器访问:http://www.example.com/index.html
你的电脑(客户端):
IP:192.168.1.100
MAC:AA:BB:CC:DD:EE:FF
你家路由器(网关):
内网IP:192.168.1.1
内网MAC:11:22:33:44:55:66
公网IP:203.0.113.10
目标服务器:
IP:93.184.216.34
MAC:FF:EE:DD:CC:BB:AA

三、封装过程(从上到下)#

![[封装.png]]

第一层封装:应用层 → 生成 HTTP 报文#

浏览器构造 HTTP 请求报文,这是最原始的数据,是真正想发送的内容:

┌─────────────────────────────────────────────────────┐
│ HTTP 请求报文 │
│ │
│ GET /index.html HTTP/1.1\r\n │
│ Host: www.example.com\r\n │
│ User-Agent: Mozilla/5.0\r\n │
│ Accept: text/html\r\n │
│ Connection: keep-alive\r\n │
│ \r\n │
└─────────────────────────────────────────────────────┘
↓ 交给传输层

此时数据只是一段文本,没有任何寻址信息,不知道要发给谁,也不知道怎么保证到达。

第二层封装:传输层 → 加上 TCP 头部,生成 TCP 报文段#

TCP 层在 HTTP 报文前面加上 TCP 首部,主要作用是标明源端口和目的端口,以及保证可靠传输所需的序列号、确认号等信息:

┌───────────────────────────────────────────────────────────────┐
│ TCP 报文段 │
│ ┌────────────────────────────────────────────────────────┐ │
│ │ TCP 首部(20字节) │ │
│ │ 源端口:54321(浏览器随机分配的临时端口) │ │
│ │ 目的端口:80(HTTP 服务固定端口) │ │
│ │ 序列号:Seq=1000(当前数据段在字节流中的起始编号) │ │
│ │ 确认号:Ack=1(期望收到服务器的下一个字节编号) │ │
│ │ 标志位:ACK=1, PSH=1 │ │
│ │ 窗口大小:65535 │ │
│ │ 校验和:0xABCD │ │
│ └────────────────────────────────────────────────────────┘ │
│ ┌────────────────────────────────────────────────────────┐ │
│ │ 数据部分(HTTP报文) │ │
│ │ GET /index.html HTTP/1.1\r\n │ │
│ │ Host: www.example.com\r\n │ │
│ │ ... │ │
│ └────────────────────────────────────────────────────────┘ │
└───────────────────────────────────────────────────────────────┘
↓ 交给网络层

TCP 层此时只关心端到端的进程寻址和可靠性,它不知道也不关心 IP 地址,只负责加上端口信息。

注意:如果 HTTP 报文很大,TCP 层会先把它切割成多个报文段,每段加上不同的 Seq 序列号,分别独立交给网络层。接收方根据 Seq 重新排列还原。

第三层封装:网络层 → 加上 IP 头部,生成 IP 数据包#

网络层在 TCP 报文段前面加上 IP 首部,主要作用是标明源 IP 和目的 IP,为跨网络路由提供寻址依据:

┌───────────────────────────────────────────────────────────────────┐
│ IP 数据包 │
│ ┌──────────────────────────────────────────────────────────────┐ │
│ │ IP 首部(20字节) │ │
│ │ 版本:4(IPv4) │ │
│ │ 首部长度:20字节 │ │
│ │ 总长度:xxx字节 │ │
│ │ TTL:64(每经过一个路由器减1,为0则丢弃,防止无限循环) │ │
│ │ 协议:6(TCP,告诉接收方数据部分是TCP报文段) │ │
│ │ 源IP:192.168.1.100(你的电脑) │ │
│ │ 目的IP:93.184.216.34(www.example.com 的服务器) │ │
│ │ 首部校验和:0xXXXX │ │
│ └──────────────────────────────────────────────────────────────┘ │
│ ┌──────────────────────────────────────────────────────────────┐ │
│ │ 数据部分(TCP报文段) │ │
│ │ [TCP首部 + HTTP报文] │ │
│ └──────────────────────────────────────────────────────────────┘ │
└───────────────────────────────────────────────────────────────────┘
↓ 交给数据链路层

网络层此时只关心主机到主机的路径寻址,它不知道也不关心 MAC 地址,只负责加上 IP 信息。

第四层封装:数据链路层 → 加上以太网帧头尾,生成以太网帧#

数据链路层在 IP 数据包前面加上以太网帧头部,在后面加上帧校验序列(FCS),主要作用是标明源 MAC 和目的 MAC,用于当前这一段链路上的寻址:

┌──────────────────────────────────────────────────────────────────────┐
│ 以太网帧 │
│ ┌────────────────────────────────────────────────────────────────┐ │
│ │ 帧头部(14字节) │ │
│ │ 目的MAC:11:22:33:44:55:66(网关/路由器的MAC) │ │
│ │ 注意:不是服务器的MAC!因为服务器不在同一局域网 │ │
│ │ 源MAC:AA:BB:CC:DD:EE:FF(你的电脑网卡MAC) │ │
│ │ 类型:0x0800(表示数据部分是IPv4数据包) │ │
│ └────────────────────────────────────────────────────────────────┘ │
│ ┌────────────────────────────────────────────────────────────────┐ │
│ │ 数据部分(IP数据包) │ │
│ │ [IP首部 + TCP首部 + HTTP报文] │ │
│ └────────────────────────────────────────────────────────────────┘ │
│ ┌────────────────────────────────────────────────────────────────┐ │
│ │ FCS帧校验(4字节) │ │
│ │ CRC循环冗余校验,检测帧在传输中是否损坏 │ │
│ └────────────────────────────────────────────────────────────────┘ │
└──────────────────────────────────────────────────────────────────────┘
↓ 交给物理层

这里有个关键点:目的 MAC 是网关的 MAC,而不是服务器的 MAC。 因为服务器在公网,不在同一局域网内,链路层只管把数据送到下一跳(网关),由网关继续转发。

目的 MAC 怎么知道是网关的?通过 ARP 查询:先判断目的 IP(93.184.216.34)不在本子网内,确定要发给网关(192.168.1.1),再 ARP 查询网关 IP 对应的 MAC 地址。

第五层:物理层 → 转换为比特流在物理介质上传输#

数据链路层把以太网帧交给物理层,物理层将其转换为电信号、光信号或无线电磁波在物理介质(网线、光纤、WiFi)上传输:

以太网帧 → 0101001101001010110... → 电信号/光信号/无线电波

至此,封装完成,数据在物理介质上传播。

四、封装结构全景图#

把所有层的封装关系画在一起,可以看到像”俄罗斯套娃”一样的结构:

┌─────────────────────────────────────────────────────────────┐
│ 以太网帧 │
│ ┌──────┬──────┬──────┐ │
│ │目的MAC│源 MAC│类型 │ │
│ └──────┴──────┴──────┘ │
│ ┌───────────────────────────────────────────────────────┐ │
│ │ IP 数据包 │ │
│ │ ┌──────┬──────┬─────┬────┬──────────────────────┐ │ │
│ │ │版本 │TTL │协议 │... │源IP 目的IP │ │ │
│ │ └──────┴──────┴─────┴────┴──────────────────────┘ │ │
│ │ ┌─────────────────────────────────────────────────┐ │ │
│ │ │ TCP 报文段 │ │ │
│ │ │ ┌────────┬────────┬────────┬──────────────┐ │ │ │
│ │ │ │源端口 │目的端口 │序列号 │ 标志位 窗口 │ │ │ │
│ │ │ └────────┴────────┴────────┴──────────────┘ │ │ │
│ │ │ ┌─────────────────────────────────────────┐ │ │ │
│ │ │ │ HTTP 报文(应用层数据) │ │ │ │
│ │ │ │ GET /index.html HTTP/1.1 │ │ │ │
│ │ │ │ Host: www.example.com │ │ │ │
│ │ │ │ ... │ │ │ │
│ │ │ └─────────────────────────────────────────┘ │ │ │
│ │ └─────────────────────────────────────────────────┘ │ │
│ └───────────────────────────────────────────────────────┘ │
│ ┌──────┐ │
│ │ FCS │ │
│ └──────┘ │
└─────────────────────────────────────────────────────────────┘

五、中间路由器的处理过程#

数据不是直接从你的电脑飞到服务器的,中间要经过多个路由器。每个路由器的处理方式是固定的:只拆到第三层(网络层),不看 TCP 和 HTTP 的内容。

以你家路由器(网关)为例:

收到以太网帧
【数据链路层】
检查目的MAC是否是自己(11:22:33:44:55:66)→ 是
校验FCS,帧完整 → 通过
剥掉以太网帧头尾,取出IP数据包
丢弃原来的帧头(MAC地址完成使命)
【网络层】
读取IP首部
目的IP:93.184.216.34 → 不是本机IP
查路由表:93.184.216.34 走公网出口,下一跳是 ISP 路由器(假设是 1.2.3.4)
TTL:64 → 减1 → 63,重新计算IP首部校验和
做NAT:将源IP从192.168.1.100替换为公网IP 203.0.113.10,记录映射关系
【数据链路层(重新封装)】
ARP查询下一跳路由器(1.2.3.4)的MAC地址
重新封装新的以太网帧:
目的MAC:下一跳路由器的MAC(全新的MAC)
源MAC:路由器公网网卡的MAC(全新的MAC)
原来的MAC地址全部换掉
【物理层】
转换为信号发送出去

这个过程在每一个路由器上重复,IP 地址全程不变(除了 NAT 节点),MAC 地址每一跳都换

你的电脑 ──→ 家用路由器 ──→ ISP路由器 ──→ 骨干路由器 ──→ 服务器
MAC变化:
[PC的MAC → 网关MAC] [路由器MAC → ISP路由器MAC] [... → 服务器MAC]
IP变化(源IP):
192.168.1.100 → (NAT) → 203.0.113.10 → 203.0.113.10 → 203.0.113.10
目的IP始终:93.184.216.34

六、解封装过程(从下到上)#

![[解封装.png]] 数据到达目标服务器后,按照与封装完全相反的顺序,每一层剥掉自己的头部,把数据交给上层:

物理层:接收信号,还原比特流#

物理介质上的电信号/光信号
转换为数字比特流:01010011010010101...
交给数据链路层

数据链路层:解析以太网帧#

收到比特流,按以太网帧格式解析
检查目的MAC是否是本机网卡MAC(FF:EE:DD:CC:BB:AA)
→ 是,继续处理;不是则直接丢弃(这就是为什么嗅探需要开混杂模式)
用FCS校验帧完整性
→ 校验通过;失败则丢弃,不通知发送方(TCP层负责重传)
读取类型字段:0x0800 → 数据部分是IPv4数据包
剥掉帧头部和FCS
把IP数据包交给网络层

网络层:解析 IP 数据包#

收到IP数据包,解析IP首部
检查版本:IPv4
检查目的IP:93.184.216.34 → 是本机IP,继续
检查TTL:若为0则丢弃并发送ICMP超时消息,否则继续
验证IP首部校验和
读取协议字段:6 → 数据部分是TCP报文段
如果IP数据包经过分片,在此重组(根据标识符、片偏移字段)
剥掉IP首部
把TCP报文段交给传输层

传输层:解析 TCP 报文段#

收到TCP报文段,解析TCP首部
读取目的端口:80 → 交给监听80端口的进程(Web服务器)
验证TCP校验和
检查序列号Seq:确认数据顺序是否正确
→ 如果乱序,暂存等待缺失的报文段到齐后再按序交给上层
→ 如果有缺失,发送重复ACK请求重传
发送ACK确认(Ack = 收到的Seq + 数据长度)
更新接收窗口
剥掉TCP首部
把数据(HTTP报文)交给应用层

应用层:解析 HTTP 报文#

收到HTTP报文数据
Web服务器(如Nginx/Apache)解析HTTP格式:
读取请求行:GET /index.html HTTP/1.1
读取请求头:Host、User-Agent、Accept...
读取请求体(GET请求为空)
根据请求处理业务逻辑:
查找 /index.html 文件
或调用后端程序生成动态内容
构造HTTP响应报文(然后再次从上往下封装,发回给客户端)

七、完整流程总览#

【发送方:客户端】
应用层 HTTP报文
↓ +TCP头
传输层 [TCP头 | HTTP报文]
↓ +IP头
网络层 [IP头 | TCP头 | HTTP报文]
↓ +以太网帧头+FCS
链路层 [帧头 | IP头 | TCP头 | HTTP报文 | FCS]
↓ 转换为信号
物理层 ~~~~~~~~~~ 比特流 ~~~~~~~~~~
↓ 经过多个路由器(每跳重新封装链路层帧,IP层TTL-1)
物理层 ~~~~~~~~~~ 比特流 ~~~~~~~~~~
↓ 还原为帧
链路层 [帧头 | IP头 | TCP头 | HTTP报文 | FCS]
↓ 剥帧头+FCS
网络层 [IP头 | TCP头 | HTTP报文]
↓ 剥IP头
传输层 [TCP头 | HTTP报文]
↓ 剥TCP头,排序重组
应用层 HTTP报文
【接收方:服务器】

每一层都只和对等层”通信”——你的 TCP 层和服务器的 TCP 层通过序列号、确认号交流;你的 IP 层和路由器的 IP 层通过路由表交流;你的 HTTP 层和服务器的 HTTP 层通过报文内容交流。

计算机网络学习
https://fuwari.vercel.app/posts/computer-network/
作者
BIG熙
发布于
2026-02-26
许可协议
CC BY-NC-SA 4.0