Skip to main content

13 HTTP协议

HTTP 请求的准备

浏览器会将 URL 发送给 DNS 服务器,让它解析为 IP 地址,然后建立 TCP 连接。

HTTP 请求的构建

请求行

  • GET:去服务器获取资源
  • POST:主动告诉服务端一些信息
  • PUT:向指定资源位置上传最新内容
  • DELETE:用来删除资源

首部字段

key value,通过冒号分隔

  • Accept-Charset:客户端可以接受的字符集
  • Content-Type:正文的格式
  • Cache-control:控制缓存

HTTP 请求的发送

发送给每个报文段时,都需要对方有一个回应 ACK,来保证报文可靠地到达了对方。如果没有回应,TCP 层会进行重新传输,直到可以到达。

TCP 层发送每一个报文的时候,都需要加上自己的地址(即源地址)和它想要去的地方(即目标地址),将这两个信息放到 IP 头里面,交给 IP 层进行传输。

IP 层需要查看目标地址和自己是否是在同一个局域网。如果是,就发送 ARP 协议来请求这个目标地址对应的 MAC 地址,然后将源 MAC 和目标 MAC 放入 MAC 头,发送出去即可;如果不在同一个局域网,就需要发送到网关,还要需要发送 ARP 协议,来获取网关的 MAC 地址,然后将源 MAC 和网关 MAC 放入 MAC 头,发送出去。

网关收到包发现 MAC 符合,取出目标 IP 地址,根据路由协议找到下一跳的路由器,获取下一跳路由器的 MAC 地址,将包发给下一跳路由器。

到达目标的局域网,最后一跳的路由器能够发现,目标地址就在自己的某一个出口的局域网上。于是在这个局域网上发送 ARP,获得这个目标地址的 MAC 地址,将包发出去。

目标的机器发现 MAC 地址符合,就将包收起来;发现 IP 地址符合,根据 IP 头中协议项,知道自己上一层是 TCP 协议,于是解析 TCP 的头,里面有序列号,需要看一看这个序列包是不是我要的,如果是就放入缓存中然后返回一个 ACK,如果不是就丢弃。

TCP 头里面还有端口号,HTTP 的服务器正在监听这个端口号。于是,目标机器自然知道是 HTTP 服务器这个进程想要这个包,于是将包发给 HTTP 服务器。HTTP 服务器的进程看到,原来这个请求是要访问一个网页,于是就把这个网页发给客户端。

HTTP 返回的构建

构造好了返回的 HTTP 报文,接下来就是把这个报文发送出去。还是交给 Socket 去发送,还是交给 TCP 层,让 TCP 层将返回的 HTML,也分成一个个小的段,并且保证每个段都可靠到达。

这些段加上 TCP 头后会交给 IP 层,然后把刚才的发送过程反向走一遍。虽然两次不一定走相同的路径,但是逻辑过程是一样的,一直到达客户端。

客户端发现 MAC 地址符合、IP 地址符合,于是就会交给 TCP 层。根据序列号看是不是自己要的报文段,如果是,则会根据 TCP 头中的端口号,发给相应的进程。这个进程就是浏览器,浏览器作为客户端也在监听某个端口。

当浏览器拿到了 HTTP 的报文。发现返回 200,一切正常,于是就从正文中将 HTML 拿出来。HTML 是一个标准的网页格式。浏览器根据这个格式展示出网页。

HTTP 2.0

HTTP 1.1:

  • 在应用层以纯文本的形式进行通信
  • 每次通信都要带完整的 HTTP 的头

HTTP 2.0:

  • 对 HTTP 的头进行一定的压缩,把大量 key value 在两端建立一个索引表,对相同的头只发送索引表中的索引
  • 将一个 TCP 的连接中,切分成多个流,每个流都有自己的 ID,而且流可以是客户端发往服务端,也可以是服务端发往客户端。本质是一个虚拟的通道,流可以有优先级
  • 将所有的传输信息分割为更小的消息和帧,并对它们采用二进制格式编码。Header 帧用于传输 Header 内容,并且会开启一个新的流。Data 帧用来传输正文实体,多个 Data 帧属于同一个流。

HTTP 2.0 的客户端可以将多个请求分到不同的流中,然后将请求内容拆成帧,进行二进制传输。这些帧可以打散乱序发送, 然后根据每个帧首部的流标识符重新组装,并且可以根据优先级,决定优先处理哪个流的数据。

将三个请求变成三个流,将数据分成帧,乱序发送到一个 TCP 连接中。

QUIC 协议

HTTP 2.0 的问题:

  • HTTP 2.0 也是基于 TCP 协议的,TCP 协议在处理包时是有严格顺序
  • 当其中一个数据包遇到问题,TCP 连接需要等待这个包完成重传之后才能继续进行
  • 后面 stream 的帧会被前面的帧阻塞

自定义连接机制

基于 UDP,以一个 64 位的随机数作为 ID 来标识

自定义重传机制

QUIC 序列号是递增的,任何一个序列号的包只发送一次,再次发送时序号加一,RTT 计算相对准确,根据 offset 判定发送的是否为相同内容。

无阻塞的多路复用

同一条 QUIC 连接上可以创建多个 stream,来发送多个 HTTP 请求。一个连接上的多个 stream 之间没有依赖。

自定义流量控制

通过 window_update,来告诉对端它可以接受的字节数。QUIC 的窗口是适应自己的多路复用机制的,不但在一个连接上控制窗口,还在一个连接中的每个 stream 控制窗口。