URL:统一资源定位符
HTTP请求的准备
(1)浏览器将域名发送给DNS服务器,会解析为IP地址。
(2)通过三次握手建立TCP链接。在1.1中默认开启Keep-Alive,这个连接可以在请求中复用。
HTTP请求的构建
建立连接后,浏览器就要发送HTTP请求,构建完HTTP报文格式,浏览器会通过Socket交给传输层
- 请求行:定义了URL,版本号和方法
GET方法:对于网页最常用的,GET就是去服务器获取资源,资源返回的格式由服务器决定;
POST:主动告诉服务器一些信息;
PUT:更新资源内容;
DELETE:删除资源内容 - 首部字段
首部是 key value,通过冒号分隔。这里面,往往保存了一些非常重要的字段。
Accept-Charset:表示客户端可以接受的字符集
Content-Type:指正文的格式
Cache-control:用来控制缓存的。当客户端发送的请求中包含 max-age 指令时,如果判定缓存层中,资源的缓存时间数值比指定时间的数值小,那么客户端可以接受缓存的资源;当指定 max-age 值为 0,那么缓存层通常需要将请求转发给应用集群。
If-Modified-Since:也是一个关于缓存的。如果服务器的资源在某个时间之后更新了,那么客户端就应该下载最新的资源;如果没有更新,服务端会返回“304 Not Modified”的响应,那客户端就不用下载了,也会节省带宽。
HTTP请求发送
(1)HTTP 协议是基于 TCP 协议的,所以它使用面向连接的方式发送请求,通过 stream 二进制流的方式传给对方。
(2)到了 TCP 层,它会把二进制流变成一个的报文段发送给服务器。
(3)每发送一个报文,都需要对方回复一个ACK,保证报文可靠送达,如果没有回复,TCP会进行重传
(4)TCP层发送每一个报文的时候,都需要在IP头中加上源地址和目标地址
(5)IP 层需要查看目标地址和自己是否是在同一个局域网。如果是,就发送 ARP 协议来请求这个目标地址对应的 MAC 地址,然后将源 MAC 和目标 MAC 放入 MAC 头,发送出去即可;如果不在同一个局域网,就需要发送到网关,还要需要发送 ARP 协议,来获取网关的 MAC 地址,然后将源 MAC 和网关 MAC 放入 MAC 头,发送出去。
(6)网关收到包发现 MAC 符合,取出目标 IP 地址,根据路由协议找到下一跳的路由器,获取下一跳路由器的 MAC 地址,将包发给下一跳路由器。
(7)这样路由器一跳一跳终于到达目标的局域网。这个时候,最后一跳的路由器能够发现,目标地址就在自己的某一个出口的局域网上。于是,在这个局域网上发送 ARP,获得这个目标地址的 MAC 地址,将包发出去。
(8)目标的机器发现 MAC 地址符合,就将包收起来;发现 IP 地址符合,根据 IP 头中协议项,知道自己上一层是 TCP 协议,于是解析 TCP 的头,里面有***,需要看一看这个序列包是不是我要的,如果是就放入缓存中然后返回一个 ACK,如果不是就丢弃。
(9)通过TCP中的端口号,判断需要将包发送那个服务
HTTP 返回的构建
状态行
- 状态码:标识网页状态的
- 版本号:HTTP的版本号
首部K-V
- Retry-After:客户端应该在多长时间以后再次尝试一下;
- Content-Type,表示返回的是 HTML,还是 JSON;
(1)构造好了返回的 HTTP 报文,接下来就是把这个报文发送出去。还是交给 Socket 去发送,还是交给 TCP 层,让 TCP 层将返回的 HTML,也分成一个个小的段,并且保证每个段都可靠到达。
(2)这些段加上 TCP 头后会交给 IP 层,然后把刚才的发送过程反向走一遍。
(3)客户端发现 MAC 地址符合、IP 地址符合,于是就会交给 TCP 层。根据***看是不是自己要的报文段,如果是,则会根据 TCP 头中的端口号,发给相应的进程。
(4)浏览器解析HTTP报文
HTTP 2.0
(1)HTTP 2.0 会对 HTTP 的头进行一定的压缩,将原来每次都要携带的大量 key value 在两端建立一个索引表,对相同的头只发送索引表中的索引。
(2)HTTP 2.0 协议将一个 TCP 的连接中,切分成多个流,每个流都有自己的 ID,而且流可以是客户端发往服务端,也可以是服务端发往客户端。它其实只是一个虚拟的通道。流是有优先级的。
(3)HTTP 2.0 还将所有的传输信息分割为更小的消息和帧,并对它们采用二进制格式编码。常见的帧有Header 帧,用于传输 Header 内容,并且会开启一个新的流。再就是Data 帧,用来传输正文实体。多个 Data 帧属于同一个流。
通过这两种机制,HTTP 2.0 的客户端可以将多个请求分到不同的流中,然后将请求内容拆成帧,进行二进制传输。这些帧可以打散乱序发送, 然后根据每个帧首部的流标识符重新组装,并且可以根据优先级,决定优先处理哪个流的数据。
优点
(1)HTTP 2.0 成功解决了 HTTP 1.1 的队首阻塞问题,
(2)不需要通过 HTTP 1.x 的 pipeline 机制用多条 TCP 连接来实现并行请求与响应;
(3)减少了 TCP 连接数对服务器性能的影响,同时将页面的多个数据 css、js、 jpg 等通过一个数据链接进行传输,能够加快页面组件的传输速度。
QUIC 协议(HTTP 3.0)
HTTP 2.0 虽然大大增加了并发性,但还是有问题的。因为 HTTP 2.0 也是基于 TCP 协议的,TCP 协议在处理包时是有严格顺序的。当其中一个数据包遇到问题,TCP 连接需要等待这个包完成重传之后才能继续进行。虽然 HTTP 2.0 通过多个 stream,使得逻辑上一个 TCP 连接上的并行内容,进行多路数据的传输,然而这中间并没有关联的数据。一前一后,前面 stream 2 的帧没有收到,后面 stream 1 的帧也会因此阻塞。
自定义连接机制
一条 TCP 连接是由四元组标识的,分别是源 IP、源端口、目的 IP、目的端口。在移动网络时代成本较高。这在 TCP 是没有办法的,但是基于 UDP,就可以在 QUIC 自己的逻辑里面维护连接的机制,不再以四元组标识,而是以一个 64 位的随机数作为 ID 来标识,而且 UDP 是无连接的,所以当 IP 或者端口变化的时候,只要 ID 不变,就不需要重新建立连接。
自定义重传机制
TCP 为了保证可靠性,通过使用序号和应答机制,来解决顺序问题和丢包问题。任何一个序号的包发过去,都要在一定的时间内得到应答,否则一旦超时,就会重发这个序号的包。在 TCP 里面超时的采样存在不准确的问题。QUIC 也有个***,是递增的。任何一个***的包只发送一次,下次就要加一了。QUIC 定义了一个 offset 概念。QUIC 既然是面向连接的,也就像 TCP 一样,是一个数据流,发送的数据在这个数据流里面有个偏移量 offset,可以通过 offset 查看数据发送到了哪里,这样只要这个 offset 的包没有来,就要重发;如果来了,按照 offset 拼接,还是能够拼成一个流。
无阻塞的多路复用
同一条 QUIC 连接上可以创建多个 stream,来发送多个 HTTP 请求。但是,QUIC 是基于 UDP 的,一个连接上的多个 stream 之间没有依赖。
自定义流量控制
TCP 的流量控制是通过滑动窗口协议。QUIC 的流量控制也是通过 window_update,来告诉对端它可以接受的字节数。但是 QUIC 的窗口是适应自己的多路复用机制的,不但在一个连接上控制窗口,还在一个连接中的每个 stream 控制窗口。QUIC 的 ACK 是基于 offset 的,每个 offset 的包来了,进了缓存,就可以应答,应答后就不会重发,中间的空挡会等待到来或者重发即可,而窗口的起始位置为当前收到的最大 offset,从这个 offset 到当前的 stream 所能容纳的最大缓存,是真正的窗口大小。