在理解网络协议/数据包之前,你需要有一定的基础知识。
必要的基础知识(一)
IP、端口、ASN
IP(网络层):数据包发送和接收的地址,用于唯一定位一台主机或路由器节点,包含 32 位的 IPv4(如 192.0.2.1)与 128 位的 IPv6(如 2001:db8::1)。内网地址和公网地址拥有不同的 IP 段。
端口(传输层): TCP/UDP 的最终目的地,范围是 0-65535,端口用于区分同一主机上的不同应用进程 (常见端口有 TCP 22: SSH、UDP 53: DNS 、 TCP 80: HTTP、TCP 443: HTTPS、UDP 443: QUIC 等)。
ASN(自治系统编号,Autonomous System Number):全球互联网由成千上万个拥有统一路由管理策略的大型网络(如运营商中国电信/AT&T、大型云厂商 AWS/Cloudflare)拼接而成,每一个独立自治网络称为一个 AS(自治系统)。ASN 是分给它们的唯一编号,用路由协议 (BGP) 计算数据包如何跨网传输。
就像送快递,快递是数据包,IP 就是收发货的具体楼栋地址,端口是房间门牌号,而 ASN 则代表负责派送的快递公司(顺丰之类的)。
IP 有一个非常重要的区别:内网地址和公网地址。
你在命令行输入 ipconfig 或 ip addr 时,电脑上显示的地址可能是 192.168.1.10 ( 192.168.0.0/16 ) ,常见内网 IPv4 地址段还有 172.16.0.0/12、10.0.0.0/8。
家里不同的设备都使用同一网段的内网地址,无法直接在公网路由。家用 IPv4 网络通常依赖路由器的 NAT(网络地址转换) 技术,路由器将内网多台设备发出的连接统一转换为路由器的公网 IP 发出,并在本地维护一张端口映射表来记录对应关系。
所以,网站看到的源 IP,通常是你家路由器或上游 NAT 设备使用的公网地址,而不是电脑上的 192.168.1.10。

DNS:域名如何变成连接地址
我们习惯记忆域名(如 www.example.com),但网络层建连必须依赖 IP 地址。
DNS(Domain Name System,域名系统) 是一套分布式查询系统,负责把域名解析成连接所需的信息,但它本身不负责建立网站连接。
最常见的地址记录是:
| 记录类型 | 作用 |
|---|---|
| A | 返回 IPv4 地址 |
| AAAA | 返回 IPv6 地址 |
| CNAME | 表示这个名字是另一个名字的别名 |
| HTTPS / SVCB | 现代扩展记录,除了地址外,还能直接提供端口、协议提示(如直接走 h3)和 ECH配置等参数 |
例如:访问 www.example.com,浏览器给 DNS 服务器发查询请求,DNS 返回一个 IP 地址 203.0.113.80,浏览器拿到地址后,结合协议默认端口(如 HTTPS 默认 443),才会连接这个地址上的服务。
加密 DNS
传统 DNS 通常使用 UDP 53。查询内容一般没有加密,路径上能够观察这些数据包的设备(运营商、审查设备),可能看到你查询的域名。
常见的加密 DNS 方式包括:
- DoT (DNS over TLS):通过 TLS 隧道传输 DNS(通常为 TCP 853)。
- DoH (DNS over HTTPS):将 DNS 查询伪装在标准的 HTTPS 请求中传输(TCP/UDP 443)。
- DoQ (DNS over QUIC):通过 QUIC 协议传输 DNS(通常为 UDP 853)。
加密以后,你与解析器之间的网络观察者通常不能直接读取查询内容,但递归解析器仍然知道你查询了什么。
此外,目标连接的 IP、连接时间和流量大小也不会因为 DNS 加密而自动隐藏。
DNS 污染
所谓 DNS 污染,是指当你查询某个特定域名时,收到了被恶意伪造的错误 IP,导致无法与真实网站建立连接。
一种是在无 TLS 下 (UDP 53) 路径上的审查设备直接抢答伪造 IP 直接返回,一种是 DNS 服务器缓存被污染了,给你返回错误的 IP。
比如访问 github.com,DNS 服务器 223.5.5.5:

本应该解析到 ASN 是 microsoft.com ASN 的 IP,图中新疆地区明显拿到了错误的 IP ,这就是典型的对Github 进行了DNS 污染。
代理场景的 DNS 解析
有两种常见方式:
- 本地解析:你的设备查域名 → 得到 IP→ 把 IP 交给代理服务器建立连接
- 远端解析:你的设备把网站域名交给代理服务器 → 代理服务器查询 DNS → 代理服务器连接目标。
这会影响:
- 哪一侧的 DNS 解析器会记录你的访问意图
- 本地接入网(运营商/防火墙)能否看到针对该域名的 DNS 查询或触发本地 DNS 污染
- 最终分配到哪里的 CDN 节点(本地解析可能拿到针对国内优化的 CDN,也可能是被污染的假 IP)
很多所谓的“DNS 泄漏”,就是用户希望目标域名通过代理查 DNS ,实际却在本地发出了 DNS 查询记录,可以在 browserleaks 查看你的 DNS 服务器以及有没有 DNS 泄露。
代理服务器自身的域名解析与目标网站的域名解析是两种情况。如果你的代理节点填的是一个域名,你的设备在连上代理之前,依然得先通过某种本地方式把这个节点域名解析成 IP。
TCP、UDP 与 QUIC
- TCP:面向连接,提供可靠、有序的字节流传输。
- UDP:无连接,提供简单、无状态的独立数据报传输。
- QUIC:构建在 UDP 之上,在用户态自己实现了连接管理、多路复用、可靠重传与 TLS 1.3 加密。
QUIC 很适合与 TCP 比较,但它在传输层是 QUIC 放在 UDP 里面。
TCP:一条有编号、会重传的连续传送带
假设应用要发送 ABCDEFGHIJ ,网络中可能出现丢失、重复和乱序。TCP 使用序号、确认(ACK)、重传等机制,在正常工作时把它整理成可靠、有序的字节流交给接收方。
例如:
| |
在 DEF 重传补齐之前,操作系统内核的 TCP 协议栈会暂存 GHIJ,不会把它们提前交给应用,这就是后面提到的队头阻塞。此外,TCP 还具备流量控制(防止接收方处理不过来)和拥塞控制(防止把网络管道塞爆)。
有握手和重传就意味着 TCP 适合 Web 浏览、文件传输、API 等对可靠性要求极高的场景。
TCP 有三次握手建立连接,加 TLS 需累加握手耗时:
| |
可以粗略理解为双方确认通信状态、交换初始序号并建立连接状态。这个握手没有完成网站身份验证,也没有自动给数据加密。
另一个必须理解的特点是:TCP 没有规定应用消息怎么分割。 发送方分两次发送:
| |
接收方可能一次读到并后的数据(粘包):
| |
也可能因为网络分片或缓冲区原因,分三次才读完(拆包):
| |
所以,使用 TCP 的应用协议需要自己设计分帧结构,例如使用长度字段、分隔符或者固定格式。这也是理解代理协议数据包格式的基础:协议中为什么经常会出现 长度、类型 这些字段。
UDP:一次次独立投递的数据包
UDP 无需像 TCP 一样握手,优势就是极低开销与延迟,但是丢包不重传、无原生拥塞控制,需应用层自理。
UDP 本身不负责保证:
- 数据一定到达;
- 数据按顺序到达;
- 丢失的数据自动重传;
- 对方已经建立了类似 TCP 的连接状态。
例如:
| |
UDP 不会自动替应用补上数据包 2,也不会替应用排序。
如果应用需要可靠性、拥塞控制或会话管理,就要在 UDP 之上设计相应机制 (例如 QUIC、KCP)。
UDP 非常适合实时音视频、在线游戏等场景,因为应用可以自己决定如何处理丢包和乱序。例如语音中,一个很晚才补回来的片段,可能已经没有播放价值。
但 UDP 在实际网络中不一定比 TCP 快。中国国内运营商就经常对跨网或跨境的 UDP 包进行严格的 QoS 限速甚至随机丢包。实际表现取决于应用设计、网络条件、丢包情况及上层机制。
QUIC 在 UDP 之上更完整的传输机制
QUIC 不只是把 TCP 数据简单装进 UDP。它自己实现了连接管理、确认、丢失恢复、拥塞控制和多流复用,并结合 TLS 1.3 建立安全连接。
QUIC 的握手耗时缩短至 1-RTT 或 0-RTT(快速重连)。
QUIC 的优点是应用层单连接多流解耦 (比如同一条 QUIC 连接,有三个流,一个请求网页,一个请求图片,一个请求 JS),彻底消除了传输层队头阻塞;支持基于 Connection ID 的无缝连接迁移(如 Wi-Fi 切蜂窝网络不中断)。
QUIC 一个流内的数据通常仍然需要按顺序交付。但流 A 的某段数据丢失时,流 B 已完整收到的数据,可以继续交付,这缓解了 TCP 单一有序字节流造成的跨流等待。
不过,多个 QUIC 流底层仍然共享同一条物理网络路径和同一个拥塞控制窗口,一个 UDP 包也可能包含多个流的数据。因此,丢包依然可能影响多个请求,只是阻塞机制有所不同,不会像 TCP 一样丢一个包整条连接等待。
HTTP/1.1、HTTP/2 与 HTTP/3
HTTP 定义了浏览器与网站之间如何表达请求和响应。现在访问一个普通网页,大部分请求都是通过 HTTP 协议传输的。
| 版本 | 常见传输方式 | 主要组织方式 |
|---|---|---|
| HTTP/1.1 | TCP;HTTPS 时再使用 TLS | 纯文本格式的请求行、头部与正文 |
| HTTP/2 | 通常为 TLS + TCP | 二进制分帧、单连接多流复用、HPACK 头部压缩 |
| HTTP/3 | QUIC,底层是 UDP | 映射到 QUIC 流、头部压缩 |
注:HTTP/2 规范虽然允许明文运行),但主流浏览器仅支持基于 TLS 的 HTTP/2;而 HTTP/3 底层的 QUIC 则强制要求 TLS 1.3。
HTTP/1.1 纯文本协议
它长这样:
| |
GET 是 HTTP 规定的请求方法,RESTful API 还规定了 POST、PUT、DELETE等。
/article 是 HTTP 规定的请求路径,后面 HTTP/1.1 是协议版本。
下面的Host、User-Agent 是请求头,比如网站认证用的 Authorization: Bearer token 就是请求头里的一行。
它表示客户端希望获取 /article。
它的瓶颈应用层队头阻塞。在同一个 TCP 连接上,请求只能串行响应(必须等上一个请求完整返回,才能发送下一个响应),为了缓解这个问题,浏览器通常会对同一个域名并发建立最多 6 条独立的 TCP 连接。
HTTP/2 把一条连接分成多个逻辑流
HTTP/2 基于 TCP,引入二进制分帧层,将数据切分成帧,实现单连接多路复用,多个请求和响应在同一条 TCP 连接中交错并发传输,配合 HPACK 算法对重复的 HTTP 头部进行压缩。
也就是不同请求的数据被组织成帧,帧中带有 Stream ID。接收方只需根据帧头里的 Stream ID 把交错到达的数据分配回各个请求,这样就实现了单连接多路复用。
它的瓶颈转到了 TCP 层队头阻塞。底层仍是单一 TCP 连接,网络一旦丢包,TCP 的滑动窗口会停滞等待重传,导致所有并发 Stream 全部受阻。
HTTP/3 抛弃 TCP,拥抱 QUIC (UDP)
在 HTTP/3 中,不同的 HTTP 请求直接映射到底层不同的 QUIC 流上,并将头部压缩算法升级为适配乱序传输的 QPACK。由于 QUIC 在传输层就已经实现了各流独立排序与重传,单一请求丢包只会导致该请求对应的流等待,其他请求完全不受干扰。
这里的改进可以概括为:
- HTTP/1:一个 TCP 一个 HTTP 流,浏览器建立多条 TCP 连接
- HTTP/2:多个 HTTP 流,共用一条 TCP 有序字节流
- HTTP/3:多个 HTTP 流直接跑在独立的 QUIC 流上
HTTP/3 仍然可能因为共享拥塞控制、应用依赖或头部压缩状态等原因发生等待,并未彻底消除了所有阻塞。
现代代理协议为了对抗审查,极少再裸奔自定义二进制格式,而是大量伪装成 HTTP 连接(例如借用 HTTP/1.1 升级机制的 WebSocket、基于 HTTP/2 的 gRPC/H2 传输,以及伪装成 HTTP/3 网站的 Hysteria2 / TUIC)。此外,代理中的多路复用(Mux)与连接并发特征,也与 HTTP/2、HTTP/3 的机制相关。
协议封装、外层流量与内层流量
协议封装指的是应用层协议,比如 HTTP/1.1 就是把内容按照 HTTP 规定的格式塞进 TCP 除了头之类的正文里传输。
例如一个 HTTPS 包的结构:

代理加入以后,会出现外层通信和内层通信。 假设这条 HTTPS 连接经过了基于 TLS 协议的代理:

外层 TLS 你的设备与代理服务器之间建立,在代理服务器结束。代理解开这一层后,可以读取代理协议中的IP、SNI、认证信息等内容。如果里面承载的是浏览器与网站之间的 HTTPS,代理继续看到的仍然是内层 TLS 密文。
也就是说,在同一次网页访问中,逻辑上同时存在两层加密会话:
- 外层加密:你的设备 ↔ 代理服务器
- 内层加密:你的浏览器 ↔ 目标网站
而在传输层,TCP 连接通常被代理切成了独立的两段:
- 第一段 TCP:你的设备 ↔ 代理服务器
- 第二段 TCP:代理服务器 ↔ 目标网站
这种嵌套 TLS in TLS 的特征后续会讲到。
加密基础:AEAD、密钥交换与前向安全
AEAD 同时提供内容保密和完整性验证
AEAD(Authenticated Encryption with Associated Data,带关联数据的认证加密) 是现代对称加密的标准范式(如 AES-GCM、ChaCha20-Poly1305),不仅保证数据的机密性(加密不可读),还同时生成消息认证码(MAC)以保障完整性(防篡改);此外,它允许将未加密的元数据(如网络报头)一同纳入防篡改校验计算。
假设原始内容是 转账金额:100 ,只考虑保密性,是希望旁观者无法读取这个金额。
完整性则要求:攻击者即使截获通信,也无法把它有效修改成 转账金额:900,防止让接收方把修改后的内容当成合法消息接受。
AEAD 可以粗略理解为:
| |
不过,AEAD 也有限制:
- 它不会自动隐藏密文长度(明文多长,密文加上固定长度的 Tag 就是多长)
- 它不会自动隐藏通信双方 IP
- 它不会自动解决所有重放问题(重复发包)
- 它验证的是数据与密钥之间的关系,不能单独证明密钥持有者就是你想访问的网站
为什么需要密钥交换
对称加密(AES、ChaCha20)速度快,但前提是双方已经有同一把密钥。拿到这把密钥有两条路:
- 预共享密钥(PSK):提前约定好,Shadowsocks 的密码就属于这种。
- 密钥交换:在公开信道上现场协商出一把只有双方知道的密钥。
就像双方先公开一种底色,各自偷偷加一种只有自己知道的颜色,把混合结果交给对方,再各自往收到的混合色里加上自己的私有色。两人最后得到的颜色相同(协商出的共享密钥),而窃听者只看到两份混合色,没法把私有色分离出来。
数学表达是:公开底数 $g$ 和大素数 $p$,客户端选私钥 $a$ 发送 $g^a \bmod p$,服务端选私钥 $b$ 发送 $g^b \bmod p$,最终双方都能在本地算出相同的共享密钥 $g^{ab} \bmod p$。现代网络协议普遍采用基于椭圆曲线的 ECDH,其中最常见的是 X25519,它的公钥体积非常紧凑,只有固定的 32 字节。
DH 只解决"怎么得到共享密钥",不解决"对面是谁"。没有认证就会被中间人攻击,所以必须配合认证手段:证书签名、PSK、密码,或者 REALITY 里的服务端公钥。
前向安全
定义:长期密钥将来泄露,过去的通信仍然安全。
每次建立新连接时,双方生成一次性的临时密钥对做 DH 密钥交换,用完销毁。服务端长期私钥只负责认证,不参与加密会话密钥。
对比 TLS 1.2 里的 RSA 密钥传输:客户端用服务器公钥加密 premaster secret,如果服务器私钥日后泄露,被录下来的历史流量就能全部解开。这就是 harvest now, decrypt later。TLS 1.3 删掉了静态 RSA 密钥交换,只保留临时密钥交换,所以 TLS 1.3 一定有前向保密。
后量子
前向保密防的是长期私钥将来泄露,不防 量子计算机将来破解 ECDH。被录下来的 X25519 握手,将来同样可能被量子计算机还原。目前主流标准是混合密钥交换:X25519 + ML-KEM-768(X25519MLKEM768),只要两者有一个没被攻破,会话就是安全的。

浏览器已经在 ClientHello 里带上这个 key_share,比如 Chromium 131,Safari 26 以上。
X25519MLKEM768 key_share 从 32 字节涨到约 1216 字节,ClientHello 整体变成 1.5 KB 以上,常常被拆成两个 TCP 包。

mihomo的 uTLS 实现为了规避兼容性问题,在相关场景下回退使用了较老的 Chrome 120 指纹,而sing-box则曾用新版浏览器指纹手动 trim 掉X25519MLKEM768。这会导致ClientHello与新版 Chrome 的 JA4 指纹完全对不上(关于 TLS 指纹与 JA3/JA4 的细节,我们将在系列第三篇中深入剖析)。这种魔改ClientHello,可能变成一种特征。