Notes on Network Protocol Http
Http 总结 from 极客时间 - 趣谈网络协议
HTTP 请求的准备
- DNS解析IP
HTTP基于TCP, 首先建立TCP链接- 现在默认是
1.1协议, 默认是开启了Keep-Alive, 多次请求中复用
- 现在默认是
HTTP 请求的构建

请求行
- Request URL
- Request Method
- Version
请求首部
key value, 用冒号分隔.
Accapt-Charset表示客户端可以接受的字符集Content-Type正文的格式
HTTP 请求的发送
基于
TCP协议的, 使用面向连接的方式发送请求, 通过stream二进制流的方式传给对方.
同一个网段
IP层->ARP获取目标地址MAC, 添加MAC头(源和目标), 发送出去
不在同一个网段
IP层->ARP获取网关的MAC, 然后发送
HTTP 返回的构建

浏览器作为客户端也在监听某个进程.
HTTP 2.0
- 对
HTTP的头进行一定的压缩, 将原来每次都要携带的大量key value在两端监理一个索引表, 对相同的头只发送索引表中的索引 - 将一个
TCP链接切分成多个流, 流是有优先级的. 流是双向的, 可以是客户端给服务端, 也可以是服务端给客户端. 一个TCP链接里面 - 帧
Header帧, 传输Header内容Data帧, 传输正文实体
可以将多个请求分到不同的流中, 然后将请求内容拆成帧, 进行二进制传输. 这些帧可以打散乱序发送, 然后根据每个帧首部的流标识符重新组装, 并且可以根据优先级, 决定优先处理哪个流的数据.

- 左是
HTTP 1.1, 串行请求 - 右是
HTTP 2.0, 同时发送多个请求和回应- 将三个请求变成三个流, 将数据分成帧, 乱序发送到一个
TCP连接中
- 将三个请求变成三个流, 将数据分成帧, 乱序发送到一个

总结
2.0解决了1.1的队首阻塞问题, 同时, 也不需要通过HTTP 1.x的pipeline机制用多条TCP链接来实现并行请求与相应- 减少了
TCP连接数对服务器性能的影响, 将多个数据如css, js, jpg等通过一个数据链接进行传输
QUIC 协议
from google
TCP 协议在处理包时是有严格顺序的. 当其中一个数据包遇到问题, TCP 连接需要等待这个包完成重传. 虽然 HTTP 2.0 通过多个 stream, 使得逻辑上一个 TCP 连接上的并行内容, 进行多路数据的传输, 然而这中间并没有关联的数据. 一前一后, 前面
stream 2的帧没有收到, 后面stream 1的帧也会因此阻塞.
TCP切换到UDP就是QUIC协议.
机制一: 自定义连接机制
TCP中的 源 IP,源端口,目的 IP,目的端口 一个发生变化( 比如 wifi, 手机信号不稳 ), 就需要断开重连. 在进行三次握手。
UDP用一个64位速记数作为ID标识,UDP是无连接的. 只要ID不变, 就不需要重新建立连接.
机制二: 自定义重传机制
TCP为保证可靠性, 使用序号和应答机制, 解决顺序问题和丢包问题.

TCP如果发送100两次(因为第一次没有返回), 这时候返回一个ACK 101, 代表客户端收到了. 这个RTT(采样往返时间)ACK是根据那次的发送计算. 采样不准确QUIC通过序列号递增+offset- 发送
100, 下次重发会递增序号101. 这样ACK返回就知道对应哪个了, 采样会准确 - 通过
offset来判断100和101是不是同样的内容. 通过offset拼接成一个流
- 发送
机制三: 无阻塞的多路复用
QUIC是基于UDP的, 一个连接上的多个stream之间没有依赖.
假如 stream2 丢了一个 UDP 包, 后面跟着 stream3 的一个 UDP 包, 虽然 stream2 的那个包需要重传,但是 stream3 的包无需等待, 就可以发给用户.
机制四: 自定义流量控制
TCP流量控制是通过滑动窗口协议.
QUIC是通过window_update, 来告诉对端它可以接受的字节数, 适应多路复用机制.
- 可以在一个连接上控制窗口.
- 在一个连接中的每个
stream控制窗口.
在 TCP 协议中, 接收端的窗口的起始点是下一个要接收并且 ACK 的包, 即便后来的包都到了, 放在缓存里面, 窗口也不能右移, 因为 TCP 的 ACK 机制是基于序列号的累计应答, 一旦 ACK 了一个系列号, 就说明前面的都到了, 所以只要前面的没到, 后面的到了也不能 ACK, 就会导致后面的到了, 也有可能超时重传, 浪费带宽。
QUIC 的 ACK 是基于 offset 的, 每个 offset 的包来了, 进了缓存, 就可以应答, 应答后就不会重发, 中间的空挡会等待到来或者重发即可, 而窗口的起始位置为当前收到的最大 offset, 从这个 offset 到当前的 stream 所能容纳的最大缓存, 是真正的窗口大小。显然, 这样更加准确.
