小宋爱睡觉 小宋爱睡觉
首页
  • HTML
  • CSS
  • JavaScript
  • Vue
  • React
  • 计算机网络
  • 浏览器原理
  • 性能优化
  • 设计模式
手写系列
  • 字符串
  • 数组
  • 链表
  • 树
  • 动态规划
  • 排序算法
  • GitHub (opens new window)
  • JueJin (opens new window)
首页
  • HTML
  • CSS
  • JavaScript
  • Vue
  • React
  • 计算机网络
  • 浏览器原理
  • 性能优化
  • 设计模式
手写系列
  • 字符串
  • 数组
  • 链表
  • 树
  • 动态规划
  • 排序算法
  • GitHub (opens new window)
  • JueJin (opens new window)
  • HTTP协议与请求方法
    • 常见的HTTP请求方法
    • get和post的区别
    • post和put的区别
    • 对 Accept 系列字段了解多少?
    • 常见的content-type有哪些
    • 对于定长和不定长的数据,HTTP 是怎么传输的?
    • HTTP 如何处理大文件的传输?
    • 介绍一下options请求
    • 存在多个Post请求,他们的请求头一致,但是请求体不一致,会发送几次Option请求
    • http1.0和http1.1之间有哪些区别
    • http1.1和2.0的区别
    • TCP长连接和短连接的区别
    • HTTP 中的 keep-alive 有了解吗?它和多路复用的区别
    • HTTP2的缺点
    • https与http的区别
    • GET方法对URL长度限制的原因
    • 对keep-alive的理解
    • 页面有多张图片,http怎么加载
    • 如何理解 HTTP 代理?
    • http2头部压缩算法如何实现
    • 其他相关头部字段
    • HTTP的请求报文是什么样的
    • HTTP响应报文是什么样的
    • http的优缺点
    • RTT 往返时间是什么
    • http通信时间总和是多少
    • HTTPS通信时间总和是多少
    • http性能怎么样
    • 为什么HTTP3不使用TCP而是使用UDP
    • 说一下http3.0
    • HTTP3怎么解决队头拥塞问题、0RTT有了解过吗
    • 这么厉害,那你还知道HTTP3的向前纠错机制吗
    • 那你还知道加密认证的报文吗
    • QUIC是如何进行包确认和重传的
  • HTTPS协议
  • 状态码
  • DNS和网络模型
  • TCP和UDP
  • WebSocket
  • 计算机网络
Crucials
2021-11-24

HTTP协议与请求方法

# 常见的HTTP请求方法重要

  • get 向服务器获取数据

  • post 将实体提交给指定的资源,通常造成服务器资源修改

  • put上传文件,更新数据

  • delete 删除服务器上面的对象

  • head 获取报文首部,与get相比不返回报文主体部分

  • options:询问支持的请求方法,用来跨域请求

  • connect 要求在与代理服务器通信时建立隧道,使用隧道进行tcp通信

  • trace 回显服务器收到的请求,主要用于测试或诊断

# get和post的区别特别重要

  • 应用场景不同: 一般get请求用于服务器资源不会产生影响,比如请求一个网页的资源,但是post会对服务器产生影响,比如注册用户之类的

  • 缓存不同: 浏览器一般会对get请求缓存,一般不会对post请求缓存

    • 可以在url后面拼接时间戳
  • 发送的报文格式不同: get请求的报文实体为空,post请求的报文实体一般为向服务器发送的数据

  • 幂等:get是幂等的,post不是(幂等表示执行相同的操作,结果也是相同的)

  • 安全性不同: get将参数放在url的后面,post传递的参数不在query上面

  • 请求长度: 浏览器对url的长度有限制,会影响get请求发送数据的长度

  • post支持更多的数据类型的数据

  • POST会产生两个TCP数据包:对于GET方式的请求,浏览器会把http header和data一并发送出去,服务器响应200(返回数据);而对于POST,浏览器先发送header,如果Header里面有Expect: 100-continue表示告诉服务器我的请求体很大,服务器响应100 continue,浏览器再发送data,服务器响应200 ok(返回数据)。也就是post请求,第一次将header发送过去,确认服务器和网络没问题可以服务,才会将真正的data数据提交。 因为POST需要两步,时间上消耗的要多一点。

# post和put的区别

  • put向服务端发送数据,但不会增加数据的种类,无论进行多少次put,结果并没有什么不同(理解为更新数据)

  • post向服务端发送请求,会改变数据的种类,可以理解为创建数据

# 对 Accept 系列字段了解多少?

四个部分: 数据格式、压缩方式、支持语言和字符集

数据格式

客户端支持格式: MIME (Multipurpose Internet Mail Extensions, 多用途互联网邮件扩展)。它首先用在电子邮件系统中,让邮件可以发任意类型的数据,这对于 HTTP 来说也是通用的。

因此,HTTP 从 MIME type 取了一部分来标记报文 body 部分的数据类型,这些类型体现在Content-Type这个字段,当然这是针对于发送端而言,接收端想要收到特定类型的数据,也可以用Accept字段。

具体而言,这两个字段的取值可以分为下面几类:

  • text: text/html, text/plain, text/css 等
  • image: image/gif, image/jpeg, image/png 等
  • audio/video: audio/mpeg, video/mp4 等
  • application: application/json, application/javascript, application/pdf, application/octet-stream

压缩方式

压缩方式体现在发送方的Content-Encoding字段上, 同样的,接收什么样的压缩方式体现在了接受方的Accept-Encoding字段上。这个字段的取值有下面几种:

  • gzip: 当今最流行的压缩格式

  • deflate: 另外一种著名的压缩格式

  • br: 一种专门为 HTTP 发明的压缩算法

// 发送端
Content-Encoding: gzip
// 接收端
Accept-Encoding: gzip
1
2
3
4

支持语言

对于发送方而言,还有一个Content-Language字段,在需要实现国际化的方案当中,可以用来指定支持的语言,在接受方对应的字段为Accept-Language。如:

// 发送端
Content-Language: zh-CN, zh, en
// 接收端
Accept-Language: zh-CN, zh, en
1
2
3
4

字符集

最后是一个比较特殊的字段, 在接收端对应为Accept-Charset,指定可以接受的字符集,而在发送端并没有对应的Content-Charset, 而是直接放在了Content-Type中,以 charset 属性指定。如:

// 发送端
Content-Type: text/html; charset=utf-8
// 接收端
Accept-Charset: charset=utf-8
1
2
3
4

最后以一张图来总结一下吧:

img

# 常见的content-type有哪些重要

  1. application/x-www-form-urlencoded按照key=value&key=value进行编码

  2. multipart/form-data 通常用表单上传文件

  3. application/json 服务器消息主体是序列化的JSON字符串

  4. text/xml 主要提交xml格式数据

# 对于定长和不定长的数据,HTTP 是怎么传输的?

定长包体

对于定长包体而言,发送端在传输的时候一般会带上 Content-Length, 来指明包体的长度。

如果设置长度小了,在http响应体中直接被截去,如果设置长度大了,http会导致传输失败,显示意外终止连接

不定长包体

上述是针对于定长包体,那么对于不定长包体而言是如何传输的呢?

这里就必须介绍另外一个 http 头部字段了:

Transfer-Encoding: chunked
1

表示分块传输数据,设置这个字段后会自动产生两个效果:

  • Content-Length 字段会被忽略
  • 基于长连接持续推送动态内容

响应体的结构如下所示:

chunk长度(16进制的数)
第一个chunk的内容
chunk长度(16进制的数)
第二个chunk的内容
......
0
1
2
3
4
5
6

最后是留有有一个空行的

# HTTP 如何处理大文件的传输?

HTTP采取了范围请求的解决方案,允许客户端仅仅请求一个资源的一部分。

当然,前提是服务器要支持范围请求,要支持这个功能,就必须加上这样一个响应头:

Accept-Ranges: none
1

用来告知客户端这边是支持范围请求的。

Range 字段拆解

而对于客户端而言,它需要指定请求哪一部分,通过Range这个请求头字段确定,格式为bytes=x-y。接下来就来讨论一下这个 Range 的书写格式:

  • 0-499 表示从开始到第 499 个字节。
  • 500- 表示从第 500 字节到文件终点。
  • -100 表示文件的最后100个字节。

服务器收到请求之后,首先验证范围是否合法,如果越界了那么返回416错误码,否则读取相应片段,返回 206状态码。

同时,服务器需要添加Content-Range字段,这个字段的格式根据请求头中Range字段的不同而有所差异。

具体来说,请求单段数据和请求多段数据,响应头是不一样的。

举个例子:

// 单段数据
Range: bytes=0-9
// 多段数据
Range: bytes=0-9, 30-39

1
2
3
4
5

接下来我们就分别来讨论着两种情况。

单段数据

对于单段数据的请求,返回的响应如下:

HTTP/1.1 206 Partial Content
Content-Length: 10
Accept-Ranges: bytes
Content-Range: bytes 0-9/100

i am xxxxx
1
2
3
4
5
6

值得注意的是Content-Range字段,0-9表示请求的返回,100表示资源的总大小,很好理解。

多段数据

接下来我们看看多段请求的情况。得到的响应会是下面这个形式:

HTTP/1.1 206 Partial Content
Content-Type: multipart/byteranges; boundary=00000010101
Content-Length: 189
Connection: keep-alive
Accept-Ranges: bytes


--00000010101
Content-Type: text/plain
Content-Range: bytes 0-9/96

i am xxxxx
--00000010101
Content-Type: text/plain
Content-Range: bytes 20-29/96

eex jspy e
--00000010101--
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18

这个时候出现了一个非常关键的字段Content-Type: multipart/byteranges;boundary=00000010101,它代表了信息量是这样的:

  • 请求一定是多段数据请求
  • 响应体中的分隔符是 00000010101

因此,在响应体中各段数据之间会由这里指定的分隔符分开,而且在最后的分隔末尾添上--表示结束。

# 介绍一下options请求重要

options的用途主要有两个

  • 获取服务器支持的所有http请求方法
  • 用于检查访问权限,比如在进行cors跨域资源共享的时候,对于复杂请求,就是使用options方法发送嗅探请求,判断是否有权限访问指定的资源

# 存在多个Post请求,他们的请求头一致,但是请求体不一致,会发送几次Option请求

如果服务器有设置响应头Access-Control-Max-Age(单位是秒)的话,那么他会在这个时间段内针对相同的URL和请求头,不再发送Option请求。

# http1.0和http1.1之间有哪些区别非常重要

  • 连接方面:http1.0默认支持非持久连接,而http1.1默认支持持久连接,http1.1通过使用持久连接来使多个http请求复用同一个tcp连接,避免使用非持久连接每次需要建立的时延

  • 资源请求方面:http1.0方面存在浪费带宽(客户端只是需要某个对象的一部分,但是服务器却将整个对象送来了,并且不支持断点续传功能),http1.1在请求头引入range头域,它允许之请求资源的某个部分,即返回码是206(partial content),便于开发者自由选择充分利用带宽

  • 缓存方面:在http1.0主要使用header里的cache-control、expire作为缓存标准判断,http1.1加入了Etag、If-none-match Last-Modified、If-Modified-Since等更多可供选择的缓存头来控制缓存策略

  • http1.1新增 host字段,用来指定服务器的域名,http1.0认为每台服务器都绑定一个唯一的ip,所以请求url没有传递主机名,但是随着技术发展,在一台物理机器可以存在多台虚拟主机,共享同一个ip地址,这样可以将请求发往同一台服务器的不同网站

  • 1.1新增put、head、options等方法

# http1.1和2.0的区别非常重要

  • 二进制编码:2.0是一个二进制协议,在1.1版本中,报文头信息必须是文本(ASCII编码),数据可以是文本,也可以是二进制,2.0的头信息和数据体都是二进制,统称为帧,分为头信息帧(存放头部字段)和数据帧(存放请求体数据),都是乱序的二进制帧,不存在先后关系不需要排队。

    • 乱序的二进制帧如何组装成对应报文:
      • 所谓的乱序,指的是不同ID的Stream是乱序的,对于同一个Stream ID的帧是按顺序传输的。
      • 接收方收到二进制帧后,将相同的Stream ID组装成完整的请求报文和响应报文。
      • 二进制帧中有一些字段,控制着优先级和流量控制等功能,这样子的话,就可以设置数据帧的优先级,让服务器处理重要资源,优化用户体验。
  • 多路复用:2.0实现多路复用,复用tcp连接,但在一个连接里面客户端和服务端可以同时发送多个请求或响应,而且不用按照顺序一一发送,避免了队头堵塞的问题

  • 数据流:2.0采用了数据流的概念,因为前面的多路复用讲了,不用按顺序,所以同一个连接里面的数据包,可能属于不同的请求,所以要对数据包做标记,指出他属于哪一个请求。2.0将每个请求或回应的所有数据包称为一个数据流,都有一个 独特的编号Stream ID ,数据包发送时候,都必须标记数据流id,来区分属于哪个数据流

  • 头信息压缩:因为1.1协议不带状态,每次请求都得附上所有信息,请求很多字段都是重复的,比如cookies和user agent,一模一样的内容每次请求都得带上,浪费带宽和速度,2.0使用gzip或compress压缩再发出,而客户端和服务端同时维护一张头信息表,所有字段都会存入这张表中,生成一个索引号,以后不发送相同字段 只发索引号,可以提速

  • 服务器推送:2.0允许服务器未经请求主动向客户端发送资源,交服务器推送,提前给客户端推送必要的资源,减少延迟时间,需要注意的是2.0主动推送的是静态资源,跟ws以及使用的sse向客户端发送即时数据的推送是不同的

队头堵塞

有HTTP基本的“请求 - 应答”模型导致的,http规定报文必须是一发一收,形成了先进先出的串行队列,队列请求没有优先级,只有入队的顺序,最前面的请求最先被处理,如果队首的请求因为处理的太慢了耽误了时间,那么后面的所有请求也得跟着等待,造成了队头阻塞

# TCP长连接和短连接的区别

长连接:在一个TCP连接上可以连续发送多个数据包,在TCP连接保持期间,如果没有数据包发送,需要双方发检测包以维持此连接,一般需要自己做在线维持。

短连接:指通信双方有数据交互时,就建立一个TCP连接,数据发送完成后,则断开此TCP连接,一般银行都使用短连接。

# HTTP 中的 keep-alive 有了解吗?它和多路复用的区别重要

HTTP/1.x 是基于文本的,只能整体去传;HTTP/2 是基于二进制流的,可以分解为独立的帧,交错发送

HTTP/1.x keep-alive 必须按照请求发送的顺序返回响应;HTTP/2 多路复用不按序响应

HTTP/1.x keep-alive 为了解决队头阻塞,将同一个页面的资源分散到不同域名下,开启了多个 TCP 连接;HTTP/2 同域名下所有通信都在单个连接上完成

HTTP/1.x keep-alive 单个 TCP 连接在同一时刻只能处理一个请求(两个请求的生命周期不能重叠);HTTP/2 单个 TCP 同一时刻可以发送多个请求和响应

# HTTP2的缺点重要

  • TCP 以及 TCP+TLS 建立连接的延时,HTTP/2使用TCP协议来传输的,而如果使用HTTPS的话,还需要使用TLS协议进行安全传输,而使用TLS也需要一个握手过程,在传输数据之前,导致我们需要花掉 3~4 个 RTT。

  • TCP的队头阻塞并没有彻底解决。在HTTP/2中,多个请求是跑在一个TCP管道中的。但当HTTP/2出现丢包时,整个 TCP 都要开始等待重传,那么就会阻塞该TCP连接中的所有请求。

# https与http的区别特别重要

  • https需要ca证书,http不用

  • http是超文本传输协议,是明文传输,https是具有安全性的ssl加密传输

  • http端口是80 https是443

  • http是无状态的协议,https是具有ssl和http协议构建的可加密、身份认证的网络协议,比http安全

# GET方法对URL长度限制的原因

http不对get方法长度进行限制,其实这个限制是浏览器和服务器对url的限制,ie对url长度限制是2083字节(2k+35),ie限制地最小,所以只要不超过2083就不会有问题

GET的长度值 = URL(2083)- (你的Domain+Path)-2(2是get请求中?=两个字符的长度)
1

# 对keep-alive的理解

http1.0默认每次请求/应答,客户端和服务端都会新建一个连接,完成之后断开连接,这是短连接

使用keep-alive使客户端到服务端的连接持续有效,当出现对服务器的后续请求时,可以避免建立或重新连接,这是长连接

使用方法:

  • 1.0默认没有keep-alive,需要的话配置发送Connection: keep-alive

  • 断开的话发送Connection: close

  • 1.1版本默认保持长连接,数据传输完成的时候tcp连接不断开,等待同域名下继续使用这个通道传输数据,需要关闭就发送Connection: close

Keep-Alive的建立过程:

  • 客户端向服务器在发送请求报文同时在首部添加发送Connection字段

  • 服务器收到请求并处理 Connection字段

  • 服务器回送Connection:Keep-Alive字段给客户端

  • 客户端接收到Connection字段

  • Keep-Alive连接建立成功

服务端自动断开过程(也就是没有keep-alive):

  • 客户端向服务器只是发送内容报文(不包含Connection字段)

  • 服务器收到请求并处理

  • 服务器返回客户端请求的资源并关闭连接

  • 客户端接收资源,发现没有Connection字段,断开连接

客户端请求断开连接过程:

  • 客户端向服务器发送Connection:close字段

  • 服务器收到请求并处理connection字段

  • 服务器回送响应资源并断开连接

  • 客户端接收资源并断开连接

开启Keep-Alive的优点:

  • 较少的CPU和内存的使⽤(由于同时打开的连接的减少了);

  • 允许请求和应答的HTTP管线化;

  • 降低拥塞控制 (TCP连接减少了);

  • 减少了后续请求的延迟(⽆需再进⾏握⼿);

  • 报告错误⽆需关闭TCP连接

开启Keep-Alive的缺点:

  • 长时间的TCP连接容易导致系统资源无效占用,浪费系统资源。

# 页面有多张图片,http怎么加载

在http1,浏览器对一个域名下最大tcp连接数为6,所以会请求多次,可以用多域名部署解决,可以提高同时请求的数目,加快页面图片的获取速度

在http2中,支持多路复用,可以在一个tcp连接中发送多个http请求

# 如何理解 HTTP 代理?

我们知道在 HTTP 是基于请求-响应模型的协议,一般由客户端发请求,服务器来进行响应。

当然,也有特殊情况,就是代理服务器的情况。引入代理之后,作为代理的服务器相当于一个中间人的角色,对于客户端而言,表现为服务器进行响应;而对于源服务器,表现为客户端发起请求,具有双重身份。

那代理服务器到底是用来做什么的呢?

功能

  1. 负载均衡。客户端的请求只会先到达代理服务器,后面到底有多少源服务器,IP 都是多少,客户端是不知道的。因此,这个代理服务器可以拿到这个请求之后,可以通过特定的算法分发给不同的源服务器,让各台源服务器的负载尽量平均。当然,这样的算法有很多,包括随机算法、轮询、一致性hash、LRU(最近最少使用)等等,不过这些算法并不是本文的重点,大家有兴趣自己可以研究一下。
  2. 保障安全。利用心跳机制监控后台的服务器,一旦发现故障机就将其踢出集群。并且对于上下行的数据进行过滤,对非法 IP 限流,这些都是代理服务器的工作。
  3. 缓存代理。将内容缓存到代理服务器,使得客户端可以直接从代理服务器获得而不用到源服务器那里。

# http2头部压缩算法如何实现重要

http2头部压缩是HPACK算法,在客户端和服务端两端建立“字典”,用索引号表示重复的字符串,采用哈夫曼编码压缩整数和字符串

  • 在客户端和服务器端使用“首部表”来跟踪和存储之前发送的键值对,对于相同的数据,不再通过每次请求和响应发送;

  • 首部表在HTTP/2的连接存续期内始终存在,由客户端和服务器共同渐进地更新;

  • 每个新的首部键值对要么被追加到当前表的末尾,要么替换表中之前的值。

例如下图中的两个请求, 请求一发送了所有的头部字段,第二个请求则只需要发送差异数据,这样可以减少冗余数据,降低开销。

img

# 其他相关头部字段了解

Via

代理服务器需要标明自己的身份,在 HTTP 传输中留下自己的痕迹,怎么办呢?

通过Via字段来记录。举个例子,现在中间有两台代理服务器,在客户端发送请求后会经历这样一个过程:

客户端 -> 代理1 -> 代理2 -> 源服务器
1

在源服务器收到请求后,会在请求头拿到这个字段:

Via: proxy_server1, proxy_server2
1

而源服务器响应时,最终在客户端会拿到这样的响应头:

Via: proxy_server2, proxy_server1
1

可以看到,Via中代理的顺序即为在 HTTP 传输中报文传达的顺序。

X-Forwarded-For

字面意思就是为谁转发, 它记录的是请求方的IP地址(注意,和Via区分开,X-Forwarded-For记录的是请求方这一个IP)。

X-Real-IP

是一种获取用户真实 IP 的字段,不管中间经过多少代理,这个字段始终记录最初的客户端的IP。

相应的,还有X-Forwarded-Host和X-Forwarded-Proto,分别记录客户端(注意哦,不包括代理)的域名和协议名。

X-Forwarded-For产生的问题

前面可以看到,X-Forwarded-For这个字段记录的是请求方的 IP,这意味着每经过一个不同的代理,这个字段的名字都要变,从客户端到代理1,这个字段是客户端的IP,从代理1到代理2,这个字段就变为了代理1的 IP。

但是这会产生两个问题:

  1. 意味着代理必须解析 HTTP 请求头,然后修改,比直接转发数据性能下降。
  2. 在 HTTPS 通信加密的过程中,原始报文是不允许修改的。

由此产生了代理协议,一般使用明文版本,只需要在 HTTP 请求行上面加上这样格式的文本即可:

// PROXY + TCP4/TCP6 + 请求方地址 + 接收方地址 + 请求端口 + 接收端口
PROXY TCP4 0.0.0.1 0.0.0.2 1111 2222
GET / HTTP/1.1
...
复制代码
1
2
3
4
5

这样就可以解决X-Forwarded-For带来的问题了

# HTTP的请求报文是什么样的

由请求行 请求头部 空行 请求体组成

img

请求⾏包括:请求⽅法字段、URL字段、HTTP协议版本字段

请求头部:请求头部由关键字/值对组成,每⾏⼀对,关键字和值⽤英⽂冒号“:”分隔

  • User-Agent:产⽣请求的浏览器类型。

  • Accept:客户端可识别的内容类型列表。

  • Host:请求的主机名,允许多个域名同处⼀个IP地址,即虚拟主机。

请求体:post put等请求携带的数据

img

# HTTP响应报文是什么样的

由响应行 响应头 空行 响应体

img

# http的优缺点重要

http是超文本传输协议,定义了客户端和服务端交换报文的格式和方式,默认使用80端口,使用tcp协议作为传输层协议,保证了数据传输的可靠

优点:

  • 支持客户端/服务端

  • 简单快捷 客户向服务器请求时 只需传送请求方法和路径 由于http协议简单 使得http服务器规模小,通信速度快

  • 无连接 限制每次连接只处理一个请求,服务端处理完客户请求,客户收到应答就断开连接,可以节省传输时间

  • 无状态 状态指的是通信上下文信息,缺少状态意味着如果后续处理需要前面的信息,就必须重传,导致每次连接传送的数据量增大,另一方面在服务器不需要之前的信息他的应答就比较快

  • 灵活 http允许传输任意类型的数据对象,由content-type标记

缺点:

  • 无状态 服务器不会保存关于客户的任何消息

  • 明文传输 报文采用文本形式 不安全

  • 不安全 使用明文 内容易被窃听;不验证通信方身份,可能伪装;无法验证报文完整性,可能被篡改

# RTT 往返时间是什么

RTT(Round-Trip Time),往返时间,表示从发送端发送数据开始,到发送端收到来自接收端的确认(接收端收到数据后便立即发送确认,不包含数据传输时间)总共经历的时间,即通信一来一回的时间

# http通信时间总和是多少

上一题我们知道了RTT的概念,那么TCP的三次握手🤝理论上来说花的时间应该是1.5RTT,但是客户端第三次握手的时候不需要服务器的响应,所以节省了0.5RTT,所以TCP连接的时间为1RTT

HTTP的交易时间为

  • 一去(HTTP Request)
  • 二回 (HTTP Responses)

故 HTTP 交易时间 = 1 RTT

HTTP 通信时间总和 = TCP 建立连接时间 + HTTP 交易时间 = 1 RTT+ 1 RTT = 2 RTT

如果需要额外考虑DNS查询的话,那么还需要1RTT

以上情况是没有考虑到丢包和重传,以及无重定向和额外请求的情况

# HTTPS通信时间总和是多少

  • 一去: 客户端发送一个随机数 C,客户端的 TLS 版本号以及支持的密码套件列表给服务器端
  • 二回: 服务端收到客户端的随机值,自己也产生一个随机值 S ,并根据客户端需求的协议和加密方式来使用对应的方式,并且发送自己的证书(如果需要验证客户端证书需要说明)
  • 三去: 客户端收到服务端的证书并验证是否有效,验证通过会再生成一个随机值 pre-master,通过服务端证书的公钥去加密这个随机值并发送给服务端。如果服务端需要验证客户端证书的话会附带证书(双向认证,比如网上银行用 U 盾)
  • 四回: 服务端收到加密过的随机值并使用私钥解密获得第三个随机值,这时候两端都拥有了三个随机值,可以通过这三个随机值(C/S 加 pre-master 算出主密钥)按照之前约定的加密方式生成密钥,接下来的通信就可以通过该会话密钥来加密解密

HTTPS 基于TLS1.2通信时间总和 = TCP 建立连接时间 + TLS 连接时间 + HTTP交易时间 = 1 RTT + 2 RTT + 1 RTT = 4 RTT

HTTPS 通信时间总和(基于TLS1.3) = TCP 建立连接时间 + TLS1.3 连接时间 + HTTP交易时间 = 1 RTT + 1 RTT + 1 RTT = 3 RTT

# http性能怎么样

基于tcp/ip,使用请求和应答通信模式,所以性能来说这俩是关键

  • 长连接 可以避免每次tcp连接三次握手花费

  • http1.1 管道网络传输,就是一个tcp连接里面客户端可以发送多个请求

  • 队头拥塞 http传输是一发一收 ,里面的任务放在一个任务队列中串行执行 一旦队首请求的太慢会阻塞后面请求的处理

    解决方法:

    • 并发连接 对于一个域名允许分配多个长连接,相当于增加了任务队列
    • 域名分片 将域名分出很多个二级域名,全都指向同样的一台服务器 能够并发的长连接数变多

# 为什么HTTP3不使用TCP而是使用UDP

# 说一下http3.0

基于UDP协议实现的类似于tcp多路复用的数据流,传输可靠性,称为QUIC协议

img

  1. 流量控制、传输可靠性:QUIC在udp基础上增加一层保证数据传输可靠性,提供了数据包重传 拥塞控制等

  2. 集成TLS加密功能,减少花费的RTT数

  3. 多路复用 同一个物理连接上可以有多个独立的逻辑数据流,实现数据流单独传输,解决tcp队头阻塞问题

  4. 快速握手 基于udp,可以实现0-1个rtt来建立连接

img

# HTTP3怎么解决队头拥塞问题、0RTT有了解过吗

QUIC 协议是基于 UDP 协议实现的,在传输层层面并没有固定的连接,可以根据需要开辟任意逻辑链路, QUIC一次建立一个Connection,一个Connection下包含多个Stream流(每个stream独自维护一个逻辑连接,因为UDP层面上是无连接的),每个流对应一个文件传输,并将不同的Stream中的数据交付给不同的上层应用。QUIC的一个Connection对应多个Stream,Stream之间相互独立,因此任意一条链路断开都不会导致其他数据阻塞。

例如下图,stream2 丢了一个 UDP 包,不会影响后面跟着 Stream3 和 Stream4。这样的技术就解决了之前 TCP 存在的队头阻塞问题。

图片

并且 QUIC 在移动端的表现也会比 TCP 好。因为 TCP 是基于 IP 和端口去识别连接的,这种方式在多变的移动端网络环境下是很脆弱的。

但是 QUIC 是通过 ID 的方式去识别一个连接,不管你网络环境如何变化,只要 ID 不变,就能迅速重连上。

0RTT

通过使用类似 TCP 快速打开的技术,缓存当前会话的上下文,在下次恢复会话的时候,只需要将之前的缓存传递给服务端验证通过就可以进行传输了。

0RTT 建连可以说是 QUIC 相比 HTTP2 最大的性能优势。那什么是 0RTT 建连呢?

这里面有两层含义:

  • 传输层 0RTT 就能建立连接。
  • 加密层 0RTT 就能建立加密连接。

图片

上图左边是 HTTPS 的一次完全握手的建连过程,需要 2-3 个 RTT才开始传输数据,右边 QUIC 协议在第一个包就可以包含有效的应用数据

当然,QUIC 协议可以实现 0RTT ,但这也是有条件的,实际上是首次连接 1RTT,非首次连接 0RTT,首次连接过程:

图片

可以看到,首次连接的时候,在第 4 步时,就已经开始发送实际的业务数据了,而第 1 - 3 步正好一去一回花费了 1RTT 时间,所以,首次连接的成本是 1RTT

图片

非首次连接:

  • 前面提到客户端和服务端首次连接时服务端传递了config包,里面包含了服务端公钥和两个随机数,客户端会将config存储下来,后续再连接时可以直接使用,从而跳过这个1RTT,实现0RTT的业务数据交互。
  • 客户端保存config是有时间期限的,在config失效之后仍然需要进行首次连接时的密钥交换。

# 这么厉害,那你还知道HTTP3的向前纠错机制吗

QUIC 协议有一个非常独特的特性,称为向前纠错 (Forward Error Correction,FEC),每个数据包除了它本身的内容之外,还包括了部分其他数据包的数据,因此少量的丢包可以通过其他包的冗余数据直接组装而无需重传。

向前纠错牺牲了每个数据包可以发送数据的上限,但是减少了因为丢包导致的数据重传,因为数据重传将会消耗更多的时间(包括确认数据包丢失、请求重传、等待新数据包等步骤的时间消耗)。

假如说这次我要发送三个包,那么协议会算出这三个包的异或值并单独发出一个校验包,也就是总共发出了四个包。

当出现其中的非校验包丢包的情况时,可以通过另外三个包计算出丢失的数据包的内容。

当然这种技术只能使用在丢失一个包的情况下,如果出现丢失多个包就不能使用纠错机制了,只能使用重传的方式了。

# 那你还知道加密认证的报文吗

TCP 协议头部没有经过任何加密和认证,所以在传输过程中很容易被中间网络设备篡改,注入和窃听。比如修改序列号、滑动窗口。这些行为有可能是出于性能优化,也有可能是主动攻击。

但是 QUIC 的 packet 可以说是武装到了牙齿。除了个别报文比如 PUBLIC_RESET 和 CHLO,所有报文头部都是经过认证的,报文 Body 都是经过加密的。

这样只要对 QUIC 报文任何修改,接收端都能够及时发现,有效地降低了安全风险。

图片

如上图所示,红色部分是 Stream Frame 的报文头部,有认证。绿色部分是报文内容,全部经过加密

# QUIC是如何进行包确认和重传的

  • QUIC 的Stream流基于Stream ID+Offset进行包确认,流量控制需要保证所发送的所有包offset小于最大绝对字节偏移量(maximum absolute byte offset), 该值是基于当前已经提交的字节偏移量(offset of data consumed) 而进行确定的,QUIC会把连续的已确认的offset数据向上层应用提交。QUIC支持乱序确认,但本身也是按序(offset顺序)发送数据包。
  • QUIC利用ack frame来进行数据包的确认,来保证可靠传输。一个ack frame只包含多个确认信息,没有正文。
  • 如果数据包N超时,发送端将超时数据包N重新设置编号M(即下一个顺序的数据包编号) 后发送给接收端。
  • 在一个数据包发生超时后,其余的已经发送的数据包依旧可以基于Offset得到确认,避免了TCP利用SACK才能解决的重传问题。

其实QUIC的乱序确认设计思想并不新鲜,大量网络视频流就是通过类似的基于UDP的RUDP、RTP、UDT等协议来实现快速可靠传输的。他们同样支持乱序确认,所以就会导致这样的观看体验:明明进度条显示还有一段缓存,但是画面就是卡着不动了,如果跳过的话视频又能够播放了。

  1. 如图所示,当前缓冲区大小为8,QUIC按序(offset顺序)发送29-36的数据包:

img

  1. 31、32、34数据包先到达,基于offset被优先乱序确认,但30数据包没有确认,所以当前已提交的字节偏移量不变,缓存区不变。

img

  1. 30到达并确认,缓存区收缩到阈值,接收方发送MAX_STREAM_DATA frame(协商缓存大小的特定帧)给发送方,请求增长最大绝对字节偏移量。

img

  1. 协商完毕后最大绝对字节偏移量右移,缓存区变大,同时发送方发现数据包33超时

img

  1. 发送方将超时数据包重新编号为42继续发送

img

以上就是最基本的数据包发送-接收过程,控制数据发送的唯一限制就是最大绝对字节偏移量,该值是接收方基于当前已经提交的偏移量(连续已确认并向上层应用提交的数据包offset)和发送方协商得出。

上次更新: 2025/05/27, 15:44:05
HTTPS协议

HTTPS协议→

Copyright © 2021-2025 粤ICP备2021165371号
  • 跟随系统
  • 浅色模式
  • 深色模式