HTTP协议以及基于UDP实现可靠的协议QUIC

上古伪神

共 3590字,需浏览 8分钟

 ·

2021-09-02 09:59


  • 前言

  • HTTP协议

    • 请求准备:

    • 请求构建:

    • 请求的发送:

    • HTTP返回的构建:

  • 如何实现一个靠谱的协议?

  • QUIC协议

    • 自定义连接机制:

    • 自定义重传机制:

    • 无阻塞的多路复用:

    • 自定义流量控制:

  • 往期推荐:


前言

有三个月没更笔记文了,似乎忘了这是一个技术类公众号。

在这段时间内花了两个月重学了一遍数据结构,然后在leetcode上刷了一百多道题。

还看了一本《现代操作系统》,看的有点懵,又花钱买了一个大牛专栏看

后续将这些笔记都整理一下,发到这个公众号。

还花了一周空闲时间用python写了一个小程序,每天定时获取热点信息、自动编排、上传、发布文章。热点在文章中支持点击跳转。

之前写的网络协议其实只写了一小部分,后面还包括了HTTP协议、HTTPS协议、DNS、CDN、网络模式以及容器网络等。

那么今天就来记录一下HTTP协议相关内容。

HTTP协议

浏览器上输入url,比如http://www.xxx.com,这个叫做统一资源定位符

其中www.xxx.com是一个域名,表示互联网的一个位置。

请求准备:

浏览器会将域名发送给DNS服务器,解析成IP地址。

HTTP是基于TCP协议的,先要建立TCP连接。

目前使用的HTTP协议大部分都是1.1,在1.1的协议里面,默认开启了Keep-Alive的,这样建立的TCP连接,可以在多次请求中复用。

请求构建:

HTTP报文分为三大部分,分别是请求行,首部和请求的正文实体

请求行

URL就是http://www.xxx.com

版本就是HTTP1.1

方法有GET、POST、PUT、DELETE

首部字段

首部是Key-Value格式,通过冒号分隔,保存一些header信息

例如:Accept-Charset,表示客户端可接受的字符集。

Content-Type指的是正文的格式,例如是JSON

Cache-control 用来控制缓存

请求的发送:

HTTP 协议是基于 TCP 协议的,所以它使用面向连接的方式发送请求。

到了 TCP 层,它会转换成一个个报文段发送给服务器。

在发送给每个报文段的时候,都需要对方有一个回应 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返回的构建:

状态码有几种,1xx、2xx、3xx、4xx、5xx

首部中,Retry-After 表示,告诉客户端应该在多长时间以后再次尝试一下。

Content-Type,表示返回的是 HTML,还是 JSON。

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

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

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

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

这就是一个正常的 HTTP 请求和返回的完整过程。

如何实现一个靠谱的协议?

为了保证顺序性,每一个包都有一个ID,在建立连接的时候,会商定起始的ID是什么,然后按照ID一个个发送,为了保证不丢包,对于发送的包都要进行应答,但这个应答也不是一个一个来,而是会应答某个之前的ID,表示都收到了,这种模式称为累计确认或者累计应答

QUIC协议

QUIC协议,是Google内部的一个基于UDP的可靠传输协议。

自定义连接机制:

TCP由一个四元组确认一个连接,发生变化就得重连。移动互联网下,网络不稳定会再次重连,导致时延。

基于UDP,不再以四元组为标识,而是以一个64位的随机数作为ID来标识,而且UDP是无连接的,所以当IP或者端口变化的时候,只要ID不变,就不需要重新建立连接。

自定义重传机制:

TCP超时重传是通过自适应重传算法,通过采用往返时间RTT不断调整。

QUIC 也有个序列号,是递增的。任何一个序列号的包只发送一次,下次就要加一了。

例如,发送一个包,序号是 100,发现没有返回;再次发送的时候,序号就是 101 了;如果返回的 ACK 100,就是对第一个包的响应。如果返回 ACK 101 就是对第二个包的响应,RTT 计算相对准确。

QUIC 定义了一个 offset 概念。QUIC 既然是面向连接的,也就像 TCP 一样,是一个数据流,发送的数据在这个数据流里面有个偏移量 offset,可以通过 offset 查看数据发送到了哪里,这样只要这个 offset 的包没有来,就要重发;如果来了,按照 offset 拼接,还是能够拼成一个流。

无阻塞的多路复用:

同一条 QUIC 连接上可以创建多个 stream,来发送多个 HTTP 请求。但是,QUIC 是基于 UDP 的,一个连接上的多个 stream 之间没有依赖。这样,假如 stream2 丢了一个 UDP 包,后面跟着 stream3 的一个 UDP 包,虽然 stream2 的那个包需要重传,但是 stream3 的包无需等待,就可以发给用户。

自定义流量控制:

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

在 TCP 协议中,接收端的窗口的起始点是下一个要接收并且 ACK 的包,即便后来的包都到了,放在缓存里面,窗口也不能右移,因为 TCP 的 ACK 机制是基于序列号的累计应答,一旦 ACK 了一个序列号,就说明前面的都到了,所以只要前面的没到,后面的到了也不能 ACK,就会导致后面的到了,也有可能超时重传,浪费带宽。

QUIC 的 ACK 是基于 offset 的,每个 offset 的包来了,进了缓存,就可以应答,应答后就不会重发,中间的空档会等待到来或者重发即可,而窗口的起始位置为当前收到的最大 offset,从这个 offset 到当前的 stream 所能容纳的最大缓存,是真正的窗口大小。显然,这样更加准确。

往期推荐:

TCP为什么可靠?

详解tcp三次握手四次挥手

DNS域名解析系统

广播与多播

UDP是什么东西???

路由协议

网关和路由

星巴克气氛组电脑是如何获取IP的

浏览器发起HTTP请求后经历了什么?

推荐一个生产环境问题排查利器

MySQL系列文章

k8s全系列文章汇总


浏览 131
点赞
评论
收藏
分享

手机扫一扫分享

分享
举报
评论
图片
表情
推荐
点赞
评论
收藏
分享

手机扫一扫分享

分享
举报