网络协议杂记

2026年4月6日 · 2634 · 更新 2026年4月7日

DNS

DNS是应用层协议,基于UDP协议进行快速查询和响应,其主要功能是将人类可读的域名解析为机器可读的IP地址。DNS不是一台服务器,而是一棵倒置的树

  • 根域名服务器:树根。它不直接解析域名,但它知道所有.com、.net、.cn等顶级域名地址
  • 顶级域名服务器:负责管理.com或.org。它知道example.com的地址
  • 权威域名服务器:终点站。这是域名持有者维护的服务器,它拥有www.example.com真正的IP地址
.                           
├── com
│   └── example.com
│       └── www

当用户在浏览器中输入一个域名(如example.com)时,DNS解析查询的过程如下:

  • 第一阶段:递归查询
    • 浏览器首先检查自己的DNS缓存,如果找到了,直接返回IP地址
    • 如果浏览器没有缓存,则检查本地DNS缓存或hosts文件
    • 若自查无果,电脑会发一个包给本地DNS服务器(通常是运营商提供的)。这一步只管要结果,不管过程
  • 第二阶段:迭代查询
    • 由本地DNS去问根域名服务器,根服务器返回“下一站地址”,让你去找顶级域名服务器
    • 顶级域名服务器再让你去找权威域名服务器
    • 本地DNS最终得到答案,将其进行缓存(TTL控制),并返回给电脑
    • 电脑和浏览器也会进行缓存,然后浏览器使用获取到的IP地址发起HTTP连接

ECS

标准的DNS查询包里,源IP地址是本地DNS服务器的地址,而不是客户端的真实地址。那CDN是如何实现就近调度的呢?Google和 Akamai等巨头推动了RFC 7871协议,即ECS。当你的电脑向本地DNS服务器发起查询时,支持ECS协议的本地DNS服务器会在DNS请求的“扩展字段”里,塞进客户端的子网信息。这个信息会随着递归查询一路传给权威DNS(透明传输),权威DNS看到这个扩展字段就可以推测客户端的大致地理位置。CDN再根据策略(地理位置、网络延迟、节点负载等)返回最合适的节点IP。

HTTP/1.1

HTTP协议将数据视为一个字节流发送,它本身不需要关心数据包丢失或重传,而是基于TCP来处理所有的可靠性问题。字节流会变成一个个报文段,再通过序列号、确认应答和超时重传机制,确保每个报文段都能可靠地到达对端,即使网络中存在丢包。HTTP/1的报文分为请求报文和响应报文,这里不具体阐述了,我们来看看它所存在的问题。

  • 报文冗长:HTTP/1.1的Header是纯文本格式,且完全不压缩。每次请求都要带上冗余的请求头,对于很多只有几个字节返回值的API,Header的大小甚至远超Payload本身。不仅浪费带宽和还增加了传输时间。
  • 队头阻塞:虽然可以复用TCP连接,但一个TCP连接里的请求必须是顺序执行的,如果前面的请求阻塞,后续请求就会卡住。
  • 并发限制与TCP竞争:为了绕过队头阻塞,浏览器通常允许对同一个域名开启多个并发连接,这会导致一系列副作用。如果你要加载 100个资源,这多个连接就会频繁切换。更糟的是,每个连接都要经历TCP的慢启动,无法发挥带宽的最高效能。

HTTP/2

HTTP/2的出现弥补了HTTP/1.1的缺陷,我们来看看它的特性

  • 二进制分帧层:HTTP/2将所有传输信息分割为更小的帧,并采用二进制编码,比起文本解析要高效得多。如Header帧存储HTTP头部信息;Data帧存储实体内容。
  • 多路复用:HTTP/2能在一个TCP连接上支持任意数量的双向数据流,允许客户端和服务器同时发送多个请求和响应。每个数据流都有一个唯一的流ID,帧可以根据这个ID在乱序状态下发送,并在接收端根据流ID有序地重组为完整的HTTP消息,从而消除了HTTP/1.1的队头阻塞问题。
  • 权重与优先级:HTTP/2允许客户端为每个“流”设置优先级,对关键资源的请求可以获得更多的带宽分配。
  • 服务器推送:服务器不再是被动等待请求,它可以“预测”客户端的需求,在客户端请求之前主动推送额外的资源,减少了客户端的往返时间。
  • 头部压缩:HTTP/2使用HPACK算法进行压缩,和常用的gizp等压缩算法也不冲突,两者能结合使用。其主要原理是用一份索引表来定义常用的HTTP Header,请求的时候只需要发送在表里的索引位置即可,可以避免每次都传输冗长的头字段。
    • 静态字典:常用的Header(如Method: GET, Status: 200)用索引号代替
    • 动态字典:记录已经发送过的Header,下次再发同样的Header时,只发一个索引号即可

HTTPS

HTTPS在HTTP协议和TCP之间增加了一层TLS/SSL加密层,使得数据在网络上传输时是加密的。TLS组合使用了多种加密技术,常见的组合方式是先使用非对称加密算法让双端在不安全的网络中安全的协商出一个共享密钥,双端再基于这个共享密钥,通过密钥派生函数生成对称加密所需的会话密钥,最终双端通过会话密钥来加密传输数据。

现代主流的TLS版本是1.3,因为它只需1RTT即可建立连接。而且强制使用ECDHE密钥交换方案,这样能确保每个连接独立生成的会话密钥,而不是在连接中一直变化,从而实现前向安全。

HTTPS首次连接建立需要两个阶段的握手:TCP握手和TLS握手。当使用TLS 1.3时,这两个阶段被优化到了最快2个RTT。

HTTPS的具体握手流程这里就不过多阐述了

QUIC

QUCI协议是由Google开发的一种新的传输层协议。那Google为什么要放着现成的、工业标准的TCP不用,非要自己折腾一套基于UDP的QUIC呢?我们先来看看TCP存在的问题:

  • 更新太慢:TCP已经诞生了40多年,它的逻辑深深地根植于操作系统和中间网络设备中。当给TCP增加一个新特性时,不仅要等待全球所有的操作系统内核更新(如Linux的TCP/IP协议栈),还要等待中间网络设备升级。这导致协议演进极其缓慢
  • 队头阻塞问题:TCP的队头阻塞在丢包时会导致整个连接“假死”。在Google开发QUIC时,正值移动互联网全面取代PC端的时期,在弱网环境(如电梯、地库)下,丢包会及其频繁,连接体验明显恶化
  • 连接延迟:在TLS 1.2下,建立一个加密连接需要3RTT。在跨国访问时,通常会带来几十到几百毫秒的延迟,建连成本非常高
  • 断连痛点:在移动互联网时代,在5G和Wi-FI之间切换是常态。TCP连接由四元组唯一确定,一旦网络切换,IP改变,连接必须断开重建,这在移动端体验很差

那QUIC是如何解决这些问题的呢?

  1. QUCI构建在UDP之上,而UDP只是一个“壳”,中间设备通常只做转发,不做深度干预,且它把可靠性传输逻辑从内核搬到了用户态,功能更新也不再依赖操作系统
  2. QUIC引入了和HTTP/2类似的流机制,一个连接中可包含多个独立的流,每个流独立排序、独立重传,丢包只影响当前流,不影响其它数据,彻底解决了TCP层的队头阻塞问题
  3. 为了追求极致的0-RTT,QUIC内部集成了TLS 1.3,在握手结束后,服务端会给客户端下发一个PSK(预共享密钥)。当客户端再次连接同一服务端时,客户端会基于这个PSK本地派生出Early Data Key,使用它可以直接给服务端发送加密数据,不必等待服务端确认(抢跑)
  4. 除此之外,QUCI报文中使用了一个名为Connection ID的字段来标识目的地址,连接不再由四元组绑定,进行网络切换时无需重建连接(连接迁移)

HTTP/3

HTTP/2虽然做了大量优化,看起来已经解决了HTTP/1.1的队头阻塞问题,但实际上它只是解决了“应用层”的队头阻塞,却无法摆脱“传输层TCP”的限制。TCP是一个严格有序交付的字节流协议,如果某个TCP数据包丢失,即使后续数据已经到达,也必须等待丢失的数据重传后,才能继续向上层交付。这就导致在HTTP/2中,即使不同请求被拆分成多个流并发传输,一旦底层TCP丢包,所有流都会被阻塞。

而HTTP/3并没有在HTTP/2的基础上再做改进,而是做了一个更激进的选择,那就是直接替换底层传输协议,从TCP切换为QUIC。这使得HTTP/3能够从传输层继承QUIC的一系列特性,包括基于流的真正多路复用(避免TCP队头阻塞)、更低的连接建立延迟,以及对连接迁移的原生支持等。

HTTP/3仍然需要对重复的头部字段进行压缩,但它没有沿用HTTP/2中的HPACK,而是采用了新的QPACK。QPACK是针对QUIC的传输特性设计的,尤其是为了解决在无队头阻塞的多路复用场景下的头部压缩问题