该篇结合初学时我的笔记,以及网上的文章,辅以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三个问题
| 问题 | HTTP | HTTPS |
|---|---|---|
| 窃听 | 明文传输,可抓包看到全部内容 | 加密,抓包只能看到密文 |
| 篡改 | 中间人可修改内容 | 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)浏览器用之前生成的私钥解密算法获取网页内容。
区别
| 维度 | HTTP | HTTPS |
|---|---|---|
| 默认端口 | 80 | 443 |
| 传输内容 | 明文 | TLS 加密密文 |
| 身份认证 | 无 | 证书验证服务器身份 |
| 数据完整性 | 无保障 | MAC 校验 |
| 握手开销 | 仅 TCP 3次握手 | TCP握手 + TLS握手(额外1~2个RTT) |
| 抓包可见内容 | 完整报文 | 只能看到域名(SNI)和IP,内容加密 |
| URL | http:// | 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 的核心:
| 标志位 | 全称 | 作用 |
|---|---|---|
| SYN | Synchronize | 建立连接,握手时使用 |
| ACK | Acknowledgment | 确认号有效,连接建立后所有报文都置 1 |
| FIN | Finish | 请求断开连接,挥手时使用 |
| RST | Reset | 强制重置连接,异常断开 |
| PSH | Push | 要求接收方立即将数据交给应用层 |
| URG | Urgent | 紧急数据,配合紧急指针使用 |
-
窗口大小(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. 区别
| 维度 | TCP | UDP |
|---|---|---|
| 连接方式 | 面向连接(需握手) | 无连接(直接发) |
| 可靠性 | 可靠,保证到达、有序、不重复 | 不可靠,尽力而为 |
| 数据单位 | 字节流(无边界) | 数据报(有边界) |
| 首部大小 | 最小 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:5E00-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):
# Linux 修改 MAC 地址ip link set eth0 downip link set eth0 address 00:11:22:33:44:55ip 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 . 1003.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.1192.168.1.2 ... ← 可分配给主机的地址,共254个192.168.1.254192.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 这些地址有特殊含义,不能或不应该用于普通主机:
- 回环地址:
127.0.0.0/8最常用:127.0.0.1(localhost)发给回环地址的数据包不会到达网络,由操作系统直接在内部处理,用于本机进程间通信和网络软件测试。整个 127.0.0.0/8 都是回环地址,127.0.0.1 只是约定俗成最常用的一个。
CTF 场景:SSRF 中常用 127.0.0.1、localhost、127.1(简写形式)访问服务器本地服务,常见的绕过还包括:
http://127.0.0.1/adminhttp://localhost/adminhttp://0/admin ← 0 有时解析为 0.0.0.0,等价于 127.0.0.1http://[::1]/admin ← IPv6 回环地址http://127.1/admin ← 省略中间两组,部分系统支持http://0177.0.0.1/admin ← 八进制表示http://0x7f.0.0.1/admin ← 十六进制表示- 私有地址:
10.0.0.0/8 A类私有,范围 10.0.0.0 ~ 10.255.255.255172.16.0.0/12 B类私有,范围 172.16.0.0 ~ 172.31.255.255192.168.0.0/16 C类私有,范围 192.168.0.0 ~ 192.168.255.255私有地址只在局域网内使用,路由器不会将其路由到公网,多个局域网可以使用相同的私有地址段而不冲突,访问公网时需要经过 NAT 转换成公网 IP。
CTF 场景:SSRF 中通过访问这些地址段探测内网服务,如 http://192.168.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 的经典利用方式。
- 受限广播地址:
255.255.255.255发给这个地址的数据包被当前网络内所有主机接收,路由器不转发。主要用于 DHCP 请求(主机还没有 IP,只能广播)。
- 本机地址:
0.0.0.0含义依场景而定。作为源地址时表示”本机的任意地址”(还没分配 IP 时使用,如 DHCP 请求);作为服务监听地址时表示”监听本机所有网络接口”,比 127.0.0.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 规定的文档示例专用地址,不会在实际网络中路由,写教程、文档时用这些地址举例不会和真实地址冲突。
- 运营商级 NAT 地址:
100.64.0.0/10RFC 6598 定义,供运营商在大规模 NAT 场景下使用,介于公网和私网之间。
3.2.5 IPv4 的问题:地址耗尽
IPv4 只有 32 位,理论最大地址数约 43 亿,但扣掉各种保留、私有、广播地址后可用公网地址更少。随着互联网爆发式增长,IPv4 公网地址已于 2011 年被 IANA 分配完毕。
目前的缓解措施是 NAT(多台内网设备共享一个公网 IP),但这破坏了端到端透明性,引发了一系列问题。根本解决方案是 IPv6。
3.2.6 IPv4 地址分类
传统分类(有类寻址,了解即可):
| 类别 | 首位 | 范围 | 默认子网掩码 | 用途 |
|---|---|---|---|---|
| A 类 | 0 | 1.0.0.0 ~ 126.255.255.255 | /8 | 大型网络 |
| B 类 | 10 | 128.0.0.0 ~ 191.255.255.255 | /16 | 中型网络 |
| C 类 | 110 | 192.0.0.0 ~ 223.255.255.255 | /24 | 小型网络 |
| D 类 | 1110 | 224.0.0.0 ~ 239.255.255.255 | 无 | 组播 |
| E 类 | 1111 | 240.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.03.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 的主要差异
| 维度 | IPv4 | IPv6 |
|---|---|---|
| 地址长度 | 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 直连 eth010.0.0.0 /8 直连 eth1172.16.0.0 /12 10.0.0.254 eth10.0.0.0 /0 203.0.113.1 eth2 ← 默认路由路由器查表时使用最长前缀匹配原则:多条路由都能匹配时,选择网络前缀最长(最具体)的那条。比如目标 IP 是 192.168.1.5,同时匹配 192.168.1.0/24 和 0.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 缓存(查看命令):
# Linux/Macarp -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 en05.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/248.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:80192.168.1.101:54322 ──→ 公网IP:10001 ──→ 8.8.8.8:80192.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 层通过报文内容交流。