• HTTP协议


    一,HTTP协议简介

    HTTP协议又叫做超文本传输协议,是一种无状态无连接,以请求-响应方式运行的协议。

    也可以理解为:

    HTTP 是一个在计算机世界里专门在「两点」之间「传输」文字、图片、音频、视频等「超文本」数据的「约定和规范」。

    HTTP协议属于应用层协议,在传输层使用TCP协议,在网络层使用IP协议,在数据链路层使用MAC地址。

    • TCP协议主要解决如何在IP层之上可靠的传递数据包,使在网络上的另一端收到发端发出的所有包,并且顺序与发出顺序一致。TCP有可靠,面向连接的特点。
    • IP协议主要解决网络路由和寻址问题。
    • MAC地址主要解决相邻两台主机之间的寻址传输问题。

    一些概念

    1. 超文本:

            HTTP 传输的内容是「超文本」。

            「超文本」,它是文字、图片、视频等的混合体,最关键有超链接,能从一个超文本跳转到另外一个超文本。

            HTML 就是最常见的超文本了,它本身只是纯文字文件,但内部用很多标签定义了图片、视频等的链接,再经过浏览器的解释,呈现给我们的就是一个文字、有画面的网页了。

    2. 无状态:

            无状态是指协议对于事务处理没有记忆能力,服务器不知道客户端是什么状态。即我们给服务器发送 HTTP 请求之后,服务器根据请求,会给我们发送想要的数据过来,但是发送完,不会记录任何信息。

            HTTP的无状态是指会话的无状态,什么是会话?会话是由一组请求和响应组成的,围绕一件相关事情的请求和响应,这些不同的请求和响应之间是需要进行数据传递的,但HTTP是无状态指的是本次请求和响应,与下一次的请求和响应是没有关系的,不会发生数据传递。 比如客户在购物平台购物场景下,会有这样的场景, 登录,下单,购物,客户想要购物,需要登录后才能进行处理,单独靠HTTP是无法完成的,发起登录的HTTP请求成功登录后,不跟踪客户的登录状态,不对后续购物的请求,进行用户登录数据信息传递,是无法满足业务逻辑的,需要借助一些比如cookie技术等。

    HTTP 协议无状态特性的优点和缺点

    • 优点在于解放了服务器,每一次请求“点到为止”不会造成不必要连接占用
    • 缺点在于每次请求会传输大量重复的内容信息

    3. 无连接:

            无连接的含义是限制每次连接只处理一个请求。服务器处理完客户的请求,并收到客户的应答后,就断开连接。采用这种方式可以节省传输时间。

            早期这么做的原因是 HTTP 协议产生于互联网,因此服务器需要处理同时面向全世界数十万、上百万客户端的网页访问,但每个客户端(即浏览器)与服务器之间交换数据的间歇性较大(即传输具有突发性、瞬时性),并且网页浏览的联想性、发散性导致两次传送的数据关联性很低,大部分通道实际上会很空闲、无端占用资源。因此 HTTP 的设计者有意利用这种特点将协议设计为请求时建连接、请求完释放连接,以尽快将资源释放出来服务其他客户端。

            随着时间的推移,网页变得越来越复杂,里面可能嵌入了很多图片,这时候每次访问图片都需要建立一次 TCP 连接就显得很低效。后来,Keep-Alive 被提出用来解决这效率低的问题。

    4. URL
    4.1 URL基本概念
    • 平时我们俗称的”网址“,其实就是 URL(Uniform Resource Locator),翻译为统一资源定位符
    • 互连网上的每个文件都有一个唯一的 URL,它包含的信息指出文件的位置以及浏览器应该怎么处理它

    4.2 URL格式

    协议类型://访问资源需要的凭证信息@服务器地址:端口号/资源层级 UNIX 文件路径?查询字符串#片段标识符

    例如:

    我的博客首页:https://blog.csdn.net/weixin_60053402?type=blog

    其中:

    各个参数描述可否省略
    协议类型常见的协议类型有 http 和 https,访问 mysql 时的协议类型为 jdbc:mysql

                    可以省略

          省略后默认为 http://

    访问资源需要的凭证信息一般就是登录信息(用户名、密码等),但是现在的网站进行认证一般不再通过 URL 进行,故一般省略可以省略
    服务器地址服务器的地址可以是一个 IP 地址,也可以是一个域名(域名会通过 DNS 系统解析成一个具体的 IP 地址,可以使用 ping 域名 来得到该域名的 IP 地址),IP 地址用来描述网络上的一个具体位置,能够用来定位一个具体的主机

           在 HTML 中可以省略

    (比如 img、link、script、a 标签的 src 或者 href 属性),省略后表示服务器的 ip 或域名与当前 HTML 所属的 ip 或域名一致

    端口号端口号的主要作用是表示一台计算机中的特定进程所提供的服务,即用来区分一个主机上的不同程序。每个程序在访问网络的时候,都会关联上一个或多个端口号,通过端口号就能区分出当前的请求要给谁。

                   可以省略

    当端口号省略时,浏览器会根据协议类型自动决定使用哪个端口号(如 http 协议默认使用80端口,https 协议默认使用443端口

    资源层级 UNIX 文件路径表示访问该服务器程序上某个资源的路径

                   可以省略

               省略后相当于 /

    查询字符串查询字符串(query string)本质是一个键值对结构,且键值对之间使用 & 分割,键和值之间使用 = 分割。表示客户端给服务器传递的参数。该参数是 web 开发的一个重要参数,给前后端交互提供了很多可能性。该参数 key 和 value 的取值和个数,完全都是由程序员自己约定,因此可以通过这样的方式来自定制我们需要的信息给服务器可以省略
    片段标识符片段标识符主要用于页面内跳转,例如跳转到当前页面的某个部分、章节等等可以省略

    4.3 URL注意事项

            URL 只能使用 ASCII 字符集来通过因特网进行发送。由于 URL 常常会包含 ASCII 集合之外的字符,URL 必须转换为有效的 ASCII 格式。

    此时就需要对URL进行转义:

    将需要转码的字符转为16进制,然后从右到左,取4位(不足4位直接处理),每2位做一位,前面加上%,编码成 %XY 格式。

    例子:

    实际输入:https://www.hao123.com/

    服务器接收到:https%3A%2F%2Fwww.hao123.com%2F

    二,HTTP报文

    HTTP请求报文

    HTTP响应报文

    为什么HTTP报文需要空行?

    • 因为 HTTP 协议并没有规定报头部分的键值对有多少个,使用空行就相当于是报文的结束标记或报文和正文之间的分隔符(连续2个\r\n代表报头结束)
    • HTTP 在传输层依赖 TCP 协议,TCP 是面向字节流的。如果没有这个空行,就会出现”粘包问题“

    HTTP常见的字段

    1. 总体字段表
    字段类型名称说明实现版本

    请求方法字段

    (用于请求报文起始行)

    GET从服务器获取指定资源HTTP 1.0
    POST根据请求负荷(报文body)对指定的资源做出处理HTTP 1.0
    PUT向服务器传输文件HTTP 1.0
    HEAD获取报头headerHTTP 1.0
    DELETE请求服务器删除指定文件HTTP 1.0
    OPTIONS获取针对特定资源所支持的HTTP请求方法HTTP 1.1
    TRACE回显服务器收到的请求,主要用于测试或诊断HTTP 1.1
    CONNECT要求用隧道协议连接代理服务器HTTP 1.1
    LINK请求与服务器建立链接关系HTTP 1.1
    UNLINE断开与服务器的链接HTTP 1.1

    报头字段

    (用于请求/响应 header的字段)

    Host客户端发送请求时,用来指定服务器的域名HTTP 1.0
    Conent-Length服务器在返回数据时,会有 Content-Length 字段,表明本次响应的数据长度HTTP 1.0
    Connection常用于客户端要求服务器使用「HTTP 长连接」机制,以便其他请求复用HTTP 1.0
    Conent-Type用于服务器回应时,告诉客户端,本次数据是什么格式HTTP 1.0
    Content-Encoding表示服务器返回的数据使用了什么压缩格式HTTP 1.0

    2. 请求字段
    2.1 GET与POST
    GET:

            根据RFC规范,GET的语义是请求从服务器上获取指定的资源,这个资源可以是文本,网页,图片,视频等。一般GET请求的参数都写在URL中URL 规定只能支持 ASCII,所以 GET 请求的参数只允许 ASCII 字符 ,而且浏览器会对 URL 的长度有限制(HTTP协议本身对 URL长度并没有做任何规定)

    例如:你在我文章底部,敲入了留言后点击提交,浏览器就会执行一次 POST 请求,把你的留言文字放进了报文 body 里,然后拼接好 POST 请求头,通过 TCP 协议发送给服务器。

    POST:

            根据 RFC 规范,POST 的语义是根据请求正文对指定的资源做出处理(一般是客户端给服务器提交数据),具体的处理方式视资源类型而不同。POST 请求携带数据的位置一般是写在报文 body正文中,body 中的数据可以是任意格式的数据,只要客户端与服务端协商好即可,而且浏览器不会对 body 大小做限制。

    例如,你在我文章底部,敲入了留言后点击「提交」,浏览器就会执行一次 POST 请求,把你的留言文字放进了报文 body 里,然后拼接好 POST 请求头,通过 TCP 协议发送给服务器。

    安全和幂等概念
    • 安全:在 HTTP 协议里,所谓的「安全」是指请求方法不会「破坏」服务器上的资源。
    • 幂等:所谓的「幂等」,意思是多次执行相同的操作,结果都是「相同」的。

    GET和POST都是安全和幂等的吗?

    从RFC规范定义的语义来看:

    • GET方法是安全且幂等的因为它是「只读」操作,无论操作多少次,服务器上的数据都是安全的,且每次的结果都是相同的。所以,可以对 GET 请求的数据做缓存,这个缓存可以做到浏览器本身上(彻底避免浏览器发请求),也可以做到代理上(如nginx),而且在浏览器中 GET 请求可以保存为书签
    • POST方法既不安全也不幂等,因为是「新增或提交数据」的操作,会修改服务器上的资源,所以是不安全的,且多次提交数据就会创建多个资源,所以是不幂等的。所以,浏览器一般不会缓存 POST 请求,也不能把 POST 请求保存为书签

    注意:

            上面是根据RFC规范定义的语义来分析的,实际应用中程序员可以不i根据RFC规范语义去实现GET和POST(比如让GET实现新增或删除数据的请求)

    GET和POST有什么区别?

    GET 和 POST 其实没有本质区别,使用 GET 的场景完全可以使用 POST 代替,使用 POST 的场景一样可以使用 GET 代替。但是在具体的使用上,还是存在一些细节的区别:

    • GET 习惯上会把客户端的数据通过 URL 来传输;POST 习惯上会把客户端的数据通过 body正文 来传输
    • GET 习惯上用于从服务器获取数据;POST 习惯上是客户端给服务器提交数据
    • 一般情况,程序员会把 GET 请求的处理,实现成“幂等”的;对于 POST 请求的处理,不要求实现成“幂等”
    • GET 请求可以被缓存,可以被浏览器保存到收藏夹中;POST 请求不能被缓存

    关于 POST 比 GET 更安全的误解

    这里的安全是指信息是否会被泄漏。

    网上有一种错误的解释: 如果实现登录页面,如果使用 GET 实现登录,GET 习惯上把数据放到URL中,此时就能看到浏览器的 URL 中显示当前的用户名和密码了,所以就并不安全;而 POST 习惯上会把数据放到body正文中,因此登录时就不能直接看到用户名和密码,就安全
    正确的理解: 安全问题取决于是否加密以及加密算法的强度。这和将数据信息放到 URL 或 body正文 中无关,因为通过抓包,我们就可以得到这两部分的数据

    2.2 其他请求字段

    PUT: 与 POST 相似,但是具有幂等特性,一般用于更新
    DELETE: 删除服务器指定资源
    OPTIONS: 返回服务器所支持的请求方法
    HEAD: 与 GET 类似,只不过响应体不返回,只返回响应头
    TRACE: 能显示服务器端收到的请求,测试的时候会用到
    CONNECT: 将连接改为管道方式的代理服务器,用于SSL加密服务器的连接。

    3. 请求报头字段
    3.1 Host

    Host 的值表示客户端发送请求时,用来指定服务器主机的地址和端口(地址可以是域名,也可以是 IP;端口号可以省略或者手动指定)

    Host: www.A.com

    3.2 Content-Length

    服务器在返回数据时,会有 Content-Length 字段,表明本次回应的数据长度。

    Content-Length: 1000

    3.3 Connection 

    Connection 字段最常用于客户端要求服务器使用「HTTP 长连接」机制,以便其他请求复用。

    HTTP 长连接的特点是,只要任意一端没有明确提出断开连接,则保持 TCP 连接状态。

    HTTP/1.1 版本的默认连接都是长连接,但为了兼容老版本的 HTTP,需要指定 Connection 首部字段的值为 Keep-alive。

    Connection: Keep-alive

    开启了 HTTP Keep-Alive 机制后, 连接就不会中断,而是保持连接。当客户端发送另一个请求时,它会使用同一个连接,一直持续到客户端或服务器端提出断开连接。

    3.4 Content-Type

    Content-Type字段用于服务器回应时,告诉客户端,本次数据是什么格式

    Content-Type: text/html; charset=utf-8

    上面的类型表明,发送的是网页,而且编码是UTF-8。

    客户端请求的时候,可以使用 Accept 字段声明自己可以接受哪些数据格式。

    Accept: */*

    上面代码中,客户端声明自己可以接受任何格式的数据。

    3.5 Content-Ecoding

    Content-Ecoding字段说明数据的压缩方法。表示服务器返回的数据使用了什么压缩格式。

    Connect-Ecoding: gzip

    上面表示服务器返回的数据采用了 gzip 方式压缩,告知客户端需要用此方式解压。

    客户端在请求时,用 Accept-Ecoding 字段说明自己可以接受哪些压缩方法。

    Accept-Ecoding: gzip, deflate

    HTTP常见状态码

    1xx类

    1xx 类状态码属于提示信息,是协议处理中的一种中间状态,实际用到的比较少。

    2xx类

    2xx 类状态码表示服务器成功处理了客户端的请求,也是我们最愿意看到的状态。

    • 200 OK」是最常见的成功状态码,表示一切正常。如果是非 HEAD 请求,服务器返回的响应头都会有 body 数据。

    • 204 No Content」也是常见的成功状态码,与 200 OK 基本相同,但响应头没有body正文数据。

    • 206 Partial Content」是应用于 HTTP 分块下载或断点续传,表示响应返回的body正文数据并不是资源的全部,而是其中的一部分,也是服务器处理成功的状态。

    3xx类

    3xx 类状态码表示客户端请求的资源发生了变动,需要客户端用新的 URL 重新发送请求获取资源,也就是重定向

    • 301 Moved Permanently」表示永久重定向,说明请求的资源已经不存在了,需改用新的 URL 再次访问。

    • 302 Found」表示临时重定向,说明请求的资源还在,但暂时需要用另一个 URL 来访问。

    301 和 302 都会在响应头里使用字段 Location,指明后续要跳转的 URL,浏览器会自动重定向新的 URL。

    • 304 Not Modified」不具有跳转的含义,表示资源未修改,重定向已存在的缓冲文件,也称缓存重定向,也就是告诉客户端可以继续使用缓存资源,用于缓存控制。

    4xx类

    4xx 类状态码表示客户端发送的报文有误,服务器无法处理,也就是错误码的含义。

    • 400 Bad Request」表示客户端请求的报文有错误,但只是个笼统的错误,具体什么错我们不知道。

    • 403 Forbidden」表示服务器禁止访问资源,并不是客户端的请求出错。

    • 404 Not Found」表示请求的资源在服务器上不存在或未找到,所以无法提供给客户端。

    5xx类

    5xx 类状态码表示客户端请求报文正确,但是服务器处理时内部发生了错误,属于服务器端的错误码。

    • 500 Internal Server Error」与 400 类型,是个笼统通用的错误码,服务器发生了什么错误,我们并不知道。

    • 501 Not Implemented」表示客户端请求的功能还不支持,类似“即将开业,敬请期待”的意思。

    • 502 Bad Gateway」通常是服务器作为网关或代理时返回的错误码,表示服务器自身工作正常,访问后端服务器发生了错误。

    • 503 Service Unavailable」表示服务器当前很忙,暂时无法响应客户端,类似“网络服务正忙,请稍后重试”的意思。

    三,HTTP缓存技术

    HTTP缓存介绍

            对于一些具有重复性的 HTTP 请求,比如每次请求得到的数据都一样的,我们可以把这对「请求-响应」的数据都缓存在本地,那么下次就直接读取本地的数据,不必在通过网络获取服务器的响应了,这样的话 HTTP/1.1 的性能肯定肉眼可见的提升。所以,避免发送 HTTP 请求的方法就是通过缓存技术,HTTP 设计者早在之前就考虑到了这点,因此 HTTP 协议的头部有不少是针对缓存的字段。

    HTTP 缓存有两种实现方式,分别是强制缓存协商缓存

    强制缓存

            强制缓存指的是只要浏览器判断缓存没有过期,则直接使用浏览器的本地缓存,决定是否使用缓存的主动性在于浏览器这边。

            强制缓存是利用下面这两个 HTTP 响应头部字段实现的,它们都用来表示资源在客户端缓存的有效期:

    • Cache-Control, 是一个相对时间;
    • Expires,是一个绝对时间;

    如果 HTTP 响应头部同时有 Cache-Control 和 Expires 字段的话,Cache-Control 的优先级高于 Expires 。

    Cache-control 选项更多一些,设置更加精细,所以建议使用 Cache-Control 来实现强缓存。具体的实现流程如下:

    • 当浏览器第一次请求访问服务器资源时,服务器会在返回这个资源的同时,在 响应头部加上 Cache-Control,Cache-Control 中设置了过期时间大小;
    • 浏览器再次请求访问服务器中的该资源时,会先通过请求资源的时间与 Cache-Control 中设置的过期时间大小,来计算出该资源是否过期,如果没有过期,则使用该缓存,否则重新请求服务器;
    • 当判断过期时给服务器发送请求,服务器再次收到请求后,会再次更新 Response 头部的 Cache-Control。

    协商缓存

    当我们在浏览器使用开发者工具的时候,你可能会看到过某些请求的响应码是 304,这个是告诉浏览器可以使用本地缓存的资源,通常这种通过服务端告知客户端是否可以使用缓存的方式被称为协商缓存。

    上图就是一个协商缓存的过程,所以协商缓存就是与服务端协商之后,通过协商结果来判断是否使用本地缓存

    协商缓存的实现:

    请求头部中的 If-None-Match 字段与响应头部中的 ETag 字段,这两个字段的意思是:

    • 响应头部中 Etag:唯一标识响应资源;
    • 请求头部中的 If-None-Match:当资源过期时,浏览器发现响应头里有 Etag,则再次向服务器发起请求时,会将请求头 If-None-Match 值设置为 Etag 的值。服务器收到请求后进行比对,如果资源没有变化返回 304,如果资源变化了返回 200。

    注意,协商缓存这两个字段都需要配合强制缓存中 Cache-Control 字段来使用,只有在未能命中强制缓存的时候,才能发起带有协商缓存字段的请求

    当使用 ETag 字段实现的协商缓存的过程:

    • 当浏览器第一次请求访问服务器资源时,服务器会在返回这个资源的同时,在请求头部加上 ETag 唯一标识,这个唯一标识的值是根据当前请求的资源生成的;
    • 当浏览器再次请求访问服务器中的该资源时,首先会先检查强制缓存是否过期(响应头部的Cache-Control字段判断
      • 如果没有过期,则直接使用本地缓存;
      • 如果缓存过期了,会在请求头部加上 If-None-Match 字段,该字段的值就是 ETag 唯一标识;
    • 服务器再次收到请求后,会根据请求中的 If-None-Match 值与当前请求的资源生成的唯一标识进行比较
      • 如果值相等,则返回 304 Not Modified,不会返回资源
      • 如果不相等,则返回 200 状态码和返回资源,并在 Response 头部加上新的 ETag 唯一标识;
    • 如果浏览器收到 304 的请求响应状态码,则会从本地缓存中加载资源,否则更新资源。

    四,HTTPS协议

    什么时HTTPS协议?

            早期很多公司刚起步的时候,使用的应用层协议都是HTTP,而HTTP无论是用GET方法还是POST方法传参,都是没有经过任何加密的,因此早期很多的信息都是可以通过抓包工具抓到的,并不安全

            为了解决这个问题,于是出现了HTTPS协议,HTTPS实际就是在应用层和传输层协议之间加了一层加密层(使用SSL/TLS 安全协议),这层加密层本身也是属于应用层的,它会对用户的个人信息进行各种程度的加密。HTTPS在交付数据时先把数据交给加密层,由加密层对数据加密后再交给传输层。

            当然,通信双方使用的应用层协议必须是一样的,因此对端的应用层也必须使用HTTPS,当对端的传输层收到数据后,会先将数据交给加密层,由加密层对数据进行解密后再将数据交给应用层。

    HTTPS与HTTP有什么区别?

    • HTTP对传输的信息没有加密,不安全;HTTPS针对传输的信息加密(TCP和HTTP之间加入了 SSL/TLS 安全协议),安全。

    • HTTP 连接建立相对简单, TCP 三次握手之后便可进行 HTTP 的报文传输。而 HTTPS 在 TCP 三次握手之后,还需进行 SSL/TLS 的握手过程,才可进入加密报文传输。

    • 两者的默认端口不一样,HTTP 默认端口号是 80,HTTPS 默认端口号是 443。

    • HTTPS 协议需要向 CA(证书权威机构)申请数字证书,来保证服务器的身份是可信的。

    HTTPS具体解决了HTTP的哪些风险,又是如何解决的?

    HTTP 由于是明文传输,所以安全上存在以下三个风险:

    • 窃听风险,比如通信链路上可以获取通信内容
    • 篡改风险,比如强制植入垃圾广告
    • 冒充风险,比如冒充淘宝网站

    解决方法:

    • 混合加密:保证信息的机密性,解决了窃听的风险。
    • 摘要算法(哈希函数)保证信息的完整性,它能够为数据生成独一无二的「指纹」,指纹用于校验数据的完整性,解决了篡改的风险。
    • 数字签名算法保证信息来源的可靠性
    • 数字证书:保证服务器公钥的身份,解决冒充的风险。

    混合加密

    为了实现信息的机密性,解决窃听的风险。

    1.我们需要先了解对称加密和非对称加密的概念:

    • 对称加密:采用单钥密码系统的加密方法,同一个密钥可以同时用作信息的加密和解密。
    • 非对称加密:采用公钥和私钥来进行加密和解密,用其中一个密钥进行加密就必须用另一个密钥进行解密。

    2.HTTPS 采用的是对称加密非对称加密结合的「混合加密」方式:

    • 通信建立前采用非对称加密的方式交换「会话秘钥」,后续就不再使用非对称加密。
    • 通信过程中全部使用对称加密的「会话秘钥」的方式加密明文数据。

    3.对称加密的建立过程:

    • 通信双方建立连接的时候,双方就可以把支持的加密算法作协商,协商之后在服务器端形成非对称加密时使用的公钥和私钥,在客户端形成对称加密时使用的密钥
    • 然后服务器将公钥交给客户端(这个公钥全世界都可以看到),然后客户端用这个公钥对客户端形成的密钥进行加密,将加密后的密钥发送给服务器,服务器拿到后再用它的私钥进行解密,最终服务器就拿到了客户端的密钥。
    • 这时客户端和服务器都有了这个密钥,并且其他人是不知道的,此时客户端和服务器就可以进行对称加密通信了。

    4.采用「混合加密」的方式的原因:

    • 对称加密只使用一个密钥,运算速度快,密钥必须保密,无法做到安全的密钥交换。
    • 非对称加密使用两个密钥:公钥和私钥,公钥可以任意分发而私钥保密,解决了密钥交换问题但速度慢。

    摘要算法(哈希函数)

    为了保证传输的内容不被篡改,我们需要对内容计算出一个「指纹」,然后同内容一起传输给对方。

    对方收到后,先是对内容也计算出一个「指纹」,然后跟发送方发送的「指纹」做一个比较,如果「指纹」相同,说明内容没有被篡改,否则就可以判断出内容被篡改了。

    那么,在计算机里会用摘要算法(哈希函数)来计算出内容的哈希值,也就是内容的「指纹」,这个哈希值是唯一的,且无法通过哈希值推导出内容

    通过哈希算法可以确保内容不会被篡改,但是并不能保证「内容 + 哈希值」不会被中间人替换,因为这里缺少对客户端收到的消息是否来源于服务端的证明。所以引出了以下的数字签名算法。

    数字签名算法

    数字签名算法是基于非对称加密算法的。

    公钥和私钥是可以双向加解密的:

    • 公钥加密,私钥解密。这个目的是为了保证内容传输的安全(不被更改),因为被公钥加密的内容,其他人是无法解密的,只有持有私钥的人,才能解密出实际的内容;
    • 私钥加密,公钥解密。这个目的是为了保证消息不会被冒充,因为私钥是不可泄露的,如果公钥能正常解密出私钥加密的内容,就能证明这个消息是来源于持有私钥身份的人发送的。

    所以非对称加密的用途主要在于通过「私钥加密,公钥解密」的方式,来确认消息的身份,我们常说的数字签名算法,就是用的是这种方式,不过私钥加密内容不是内容本身,而是对内容的哈希值加密

    数字证书

    通过数字证书的方式保证服务器公钥的身份,解决冒充的风险。

    前面我们知道:

    • 可以通过哈希算法来保证消息的完整性;
    • 可以通过数字签名来保证消息的来源可靠性(能确认消息是由持有私钥的一方发送的);

    但是这还远远不够,还缺少身份验证的环节,万一公钥是被伪造的呢?

    在计算机里,有一个权威的机构就是 CA (数字证书认证机构),将服务器公钥放在数字证书(由数字证书认证机构颁发)中,只要证书是可信的,公钥就是可信的。

    HTTP各个版本

    HTTP/0.9

            HTTP协议的最初版本。

    • 没有 请求 / 响应头(Header) 和 状态码 的概念
    • 仅支持 请求方式GET
    • 仅能请求访问 HTML格式 的资源

    HTTP/1.0

    HTTP/1.0 相比 HTTP/0.9 性能上的改进:
    • 引入了 请求 / 响应头
    • 增加了 请求方式POST和HEAD
    • 不再局限于0.9版本的HTML格式,根据Content-Type可以支持多种数据格式(图片,视频等)
    • 支持缓存cache
    • 使用短连接(每次TCP连接只能发送一个请求,当服务器响应后就会关闭这次连接,下一个请求需要再次建立TCP连接)。

    HTTP/1.0的队头阻塞问题

            由于HTTP协议采取的是 请求-响应 方式,且HTTP/1.0使用短链接,因此当这次的请求发送出去后,很长时间没有响应过来,就不能再次发送其他请求。这就是HTTP1.0的队头阻塞问题。

            HTTP/1.0的队头阻塞是应用层 请求 的阻塞。

    HTTP/1.1

    HTTP/1.1 相比 HTTP/1.0 性能上的改进:
    • 默认使用长连接的方式改善了 HTTP/1.0 短连接造成的性能开销。
    • 支持管道(pipeline)网络传输,解决了请求的队头阻塞问题,只要第一个请求发出去了,不必等其回来,就可以发第二个请求出去,可以减少整体的响应时间。

    注意!!!

    实际上 HTTP/1.1 管道化技术不是默认开启,而且浏览器基本都没有支持,所以后面所有文章讨论 HTTP/1.1 都是建立在没有使用管道化的前提。大家知道有这个功能,但是没有被使用就行了。

    HTTP/1.1 性能瓶颈:
    • 请求 / 响应报头(Header)没有进行压缩就发送,首部信息越多延迟越大。只能压缩 Body正文 的部分
    • 发送冗长的首部。每次互相发送相同的首部造成的浪费较多;
    • 存在队头阻塞(应用层 响应 阻塞),服务器是按请求的顺序响应的,如果服务器响应慢,会招致客户端一直请求不到数据。
    • 请求只能从客户端开始,服务器只能被动响应

    HTTP/1.1缺点:

    1. 无状态双刃剑:

    • 无状态的好处,因为服务器不会去记忆 HTTP 的状态,所以不需要额外的资源来记录状态信息,这能减轻服务器的负担,能够把更多的 CPU 和内存用来对外提供服务。
    • 无状态的坏处,既然服务器没有记忆能力,它在完成有关联性的操作时会非常麻烦。

    2. 不安全:

    • 应用层采取HTTP协议,明文传输,可能造成数据被窥视(不加密),替换(无法证明报文的完整性),冒充(没有验证通信方的身份)

    HTTP/2.0

    相比于HTTP/1.1做了哪些优化?
    • 采用HTTPS协议
    • 头部压缩
    • 二进制格式
    • 并发传输
    • 服务器主动推送资源

    头部压缩

            HTTP/2 会压缩头(Header)如果你同时发出多个请求,他们的头是一样的或是相似的,那么,协议会帮你消除重复的部分

            这就是所谓的 HPACK 算法在客户端和服务器同时维护一张头信息表(静态/动态表),所有字段都会存入这个表,生成一个索引号,以后就不发送同样字段了,只发送索引号,这样就提高速度了。

    二进制格式

    HTTP/2 不再像 HTTP/1.1 里的纯文本形式的报文,而是全面采用了二进制格式,头信息和数据体都是二进制,并且统称为帧(frame):头信息帧和数据帧((使用「静态表、动态表、Huffman 编码」共同完成))

    这样虽然对人不友好,但是对计算机非常友好,因为计算机只懂二进制,那么收到报文后,无需再将明文的报文转成二进制,而是直接解析二进制报文,这增加了数据传输的效率

    1.静态表

    HTTP/2 为高频出现在头部的字符串和字段建立了一张静态表,它是写入到 HTTP/2 框架里的,不会变化的,静态表里共有 61 组,如下图:

    2.动态表

    静态表只包含了 61 种高频出现在头部的字符串,不在静态表范围内的头部字符串就要自行构建动态表,它的 Index 从 62 起步,会在编码解码的时候随时更新。

    比如,第一次发送时头部中的「User-Agent 」字段数据有上百个字节,经过 Huffman 编码发送出去后,客户端和服务器双方都会更新自己的动态表,添加一个新的 Index 号 62。那么在下一次发送的时候,就不用重复发这个字段的数据了,只用发 1 个字节的 Index 号就好了,因为双方都可以根据自己的动态表获取到字段的数据

    所以,使得动态表生效有一个前提:必须同一个连接上,重复传输完全相同的 HTTP 头部

    3.二进制帧

    并发传输

            我们都知道 HTTP/1.1 的实现是基于请求-响应模型的。同一个连接中,HTTP 完成一个事务(请求与响应),才能处理下一个事务,也就是说在发出请求等待响应的过程中,是没办法做其他事情的,如果响应迟迟不来,那么后续的请求是无法发送的,也造成了队头阻塞的问题。

            而 HTTP/2通过 Stream 这个设计,多个 Stream 复用一条 TCP 连接,达到并发的效果,解决了 HTTP/1.1 队头阻塞的问题,提高了 HTTP 传输的吞吐量。

    我们先来了解一下Stream,Message,Frame这三个概念:

    • Stream(单个TCP连接上的双向字节流),一个Stream包含一个或多个Message
    • Message(一个 请求 / 响应),一个Message包含一个或多个Frame
    • Frame(一个 头信息 / 数据帧),Frame是HTTP/2的基本单位

    例如:

    并且:

    • 不同 Stream 的帧是可以乱序发送的(因此可以并发不同的 Stream ),因为每个帧的头部会携带 Stream ID 信息,所以接收端可以通过 Stream ID 有序组装成 HTTP 消息,而同一 Stream 内部的帧必须是严格有序的
    • 客户端和服务器双方都可以建立 Stream,因为服务端可以主动推送资源给客户端, 客户端建立的 Stream 必须是奇数号,而服务器建立的 Stream 必须是偶数号。
    • HTTP/2 还可以对每个 Stream 设置不同优先级,帧头中的「标志位」可以设置优先级,比如客户端访问 HTML/CSS 和图片资源时,希望服务器先传递 HTML/CSS,再传图片,那么就可以通过设置 Stream 的优先级来实现,以此提高用户体验。

    总结:

    当 HTTP/2 实现 100 个并发 Stream 时,只需要建立一次 TCP 连接;

    而 HTTP/1.1 需要建立 100 个 TCP 连接,每个 TCP 连接都要经过 TCP 握手、慢启动以及 TLS 握手过程,这些都是很耗时的。

    服务器主动推送资源

    HTTP/1.1 不支持服务器主动推送资源给客户端,都是由客户端向服务器发起请求后,才能获取到服务器响应的资源。

    比如,客户端通过 HTTP/1.1 请求从服务器那获取到了 HTML 文件,而 HTML 可能还需要依赖 CSS 来渲染页面,这时客户端还要再发起获取 CSS 文件的请求,需要两次消息往返,如下图左边部分:

    如上图右边部分,在 HTTP/2 中,客户端在访问 HTML 时,服务器可以直接主动推送 CSS 文件,减少了消息传递的次数。

    在 Nginx 中,如果你希望客户端访问 /test.html 时,服务器直接推送 /test.css,那么可以这么配置:

    1. location /test.html {
    2. http2_push /test.css;
    3. }

    那 HTTP/2 的推送是怎么实现的?

    客户端发起的请求,必须使用的是奇数号 Stream服务器主动的推送,使用的是偶数号 Stream。服务器在推送资源时,会通过 PUSH_PROMISE 帧传输 HTTP 头部,并通过帧中的 Promised Stream ID 字段告知客户端,接下来会在哪个偶数号 Stream 中发送包体。

    如上图,在 Stream 1 中通知客户端 CSS 资源即将到来,然后在 Stream 2 中发送 CSS 资源,注意 Stream 1 和 2 是可以并发的。

    HTTP/2的队头阻塞问题:

            HTTP/2 是基于 TCP 协议来传输数据的,TCP 是字节流协议,TCP 层必须保证收到的字节数据是完整且连续的,这样内核才会将缓冲区里的数据返回给 HTTP 应用,那么当「前 1 个字节数据」没有到达时,后收到的字节数据只能存放在内核缓冲区里,只有等到这 1 个字节数据到达时,HTTP/2 应用层才能从内核中拿到数据,这就是 HTTP/2 队头阻塞问题。

     

            因此HTTP/2.0的队头阻塞是传输层TCP的阻塞

    总结:

    1. 对于常见的 HTTP 头部通过静态表和 Huffman 编码的方式,将体积压缩了近一半,而且针对后续的请求头部,还可以建立动态表,将体积压缩近 90%,大大提高了编码效率,同时节约了带宽资源。

      不过,动态表并非可以无限增大, 因为动态表是会占用内存的,动态表越大,内存也越大,容易影响服务器总体的并发能力,因此服务器需要限制 HTTP/2 连接时长或者请求次数。

    2. HTTP/2 实现了 Stream 并发,多个 Stream 只需复用 1 个 TCP 连接,节约了 TCP 和 TLS 握手时间,以及减少了 TCP 慢启动阶段对流量的影响。不同的 Stream ID 可以并发,即使乱序发送帧也没问题,但是同一个 Stream 里的帧必须严格有序。

      另外,可以根据资源的渲染顺序来设置 Stream 的优先级,从而提高用户体验。

    3. 服务器支持主动推送资源,大大提升了消息的传输性能,服务器推送资源时,会先发送 PUSH_PROMISE 帧,告诉客户端接下来在哪个 Stream 发送资源,然后用偶数号 Stream 发送资源给客户端。

    4. HTTP/2 通过 Stream 的并发能力,解决了 HTTP/1 队头阻塞的问题,看似很完美了,但是 HTTP/2 还是存在“队头阻塞”的问题,只不过问题不是在 HTTP 这一层面,而是在 TCP 这一层。

    HTTP/3.0

    我们先来看一下HTTP/2.0的不足之处:

            HTTP/2 通过头部压缩、二进制编码、多路Stream复用、服务器推送等新特性大幅度提升了 HTTP/1.1 的性能,而美中不足的是 HTTP/2 协议是基于 TCP 实现的,于是存在缺陷。

    • 队头阻塞(传输层TCP自身导致)
    • 网络迁移需要重新连接

    网络迁移需要重新连接:

            一个 TCP 连接是由四元组(源 IP 地址,源端口,目标 IP 地址,目标端口)确定的,这意味着如果 IP 地址或者端口变动了,就会导致需要 TCP 与 TLS 重新握手,这不利于移动设备切换网络的场景,比如 4G 网络环境切换成 WiFi。

            这些问题都是 TCP 协议固有的问题,因此HTTP/3.0把传输层协议替换成 UDP 协议!

     

    但是:

            我们知道,UDP 是一个简单、不可靠的传输协议,而且是 UDP 包之间是无序的,也没有依赖关系。而且,UDP 是不需要连接的,也就不需要握手和挥手的过程,所以天然的就比 TCP 快。

            当然,HTTP/3 不仅仅只是简单将传输协议替换成了 UDP,还基于 UDP 协议在「应用层」实现了 QUIC 协议,它具有类似 TCP 的连接管理、拥塞窗口、流量控制的网络特性,相当于将不可靠传输的 UDP 协议变成“可靠”的了,所以不用担心数据包丢失的问题

    QUIC协议解决队头阻塞:

            由于 QUIC 使用的传输协议是 UDP,UDP 不关心数据包的顺序,如果数据包丢失,UDP 也不关心。

            不过 QUIC 协议会保证数据包的可靠性,每个数据包都有一个序号唯一标识。当某个流中的一个数据包丢失了,即使该流的其他数据包到达了,数据也无法被 HTTP/3 读取,直到 QUIC 重传丢失的报文,数据才会交给 HTTP/3。

            而其他流的数据报文只要被完整接收,HTTP/3 就可以读取到数据。这与 HTTP/2 不同,HTTP/2 只要某个流中的数据包丢失了,其他流也会因此受影响。

            所以,QUIC 连接上的多个 Stream 之间并没有依赖,都是独立的,某个流发生丢包了,只会影响该流,其他流不受影响。

    QUIC协议解决连接迁移:

            在前面我们提到,基于 TCP 传输协议的 HTTP 协议,由于是通过四元组(源 IP、源端口、目的 IP、目的端口)确定一条 TCP 连接。

            那么当移动设备的网络从 4G 切换到 WiFi 时,意味着 IP 地址变化了,那么就必须要断开连接,然后重新建立连接,而建立连接的过程包含 TCP 三次握手和 TLS 四次握手的时延,以及 TCP 慢启动的减速过程,给用户的感觉就是网络突然卡顿了一下,因此连接的迁移成本是很高的。

            而 QUIC 协议没有用四元组的方式来“绑定”连接,而是通过连接 ID 来标记通信的两个端点,客户端和服务器可以各自选择一组 ID 来标记自己,因此即使移动设备的网络变化后,导致 IP 地址变化了,只要仍保有上下文信息(比如连接 ID、TLS 密钥等),就可以“无缝”地复用原连接,消除重连的成本,没有丝毫卡顿感,达到了连接迁移的功能

    总结

    HTTP/2 虽然具有多个流并发传输的能力,但是传输层是 TCP 协议,于是存在以下缺陷:

    • 队头阻塞,HTTP/2 多个请求跑在一个 TCP 连接中,如果序列号较低的 TCP 段在网络传输中丢失了,即使序列号较高的 TCP 段已经被接收了,应用层也无法从内核中读取到这部分数据,从 HTTP 视角看,就是多个请求被阻塞了;
    • 连接迁移需要重新连接,移动设备从 4G 网络环境切换到 WiFi 时,由于 TCP 是基于四元组来确认一条 TCP 连接的,那么网络环境变化后,就会导致 IP 地址或端口变化,于是 TCP 只能断开连接,然后再重新建立连接,切换网络环境的成本高;

    HTTP/3 就将传输层从 TCP 替换成了 UDP,并在 UDP 协议上开发了 QUIC 协议,来保证数据的可靠传输。

    QUIC 协议的特点:

    • 无队头阻塞,QUIC 连接上的多个 Stream 之间并没有依赖,都是独立的,也不会有底层协议限制,某个流发生丢包了,只会影响该流,其他流不受影响;
    • 连接迁移,QUIC 协议没有用四元组的方式来“绑定”连接,而是通过「连接 ID 」来标记通信的两个端点,客户端和服务器可以各自选择一组 ID 来标记自己,因此即使移动设备的网络变化后,导致 IP 地址变化了,只要仍保有上下文信息(比如连接 ID、TLS 密钥等),就可以“无缝”地复用原连接,消除重连的成本;

    面试常考:浏览器输入一个网址按下回车后发生了什么?

            当你在浏览器输入一个网址,如https://www.bilibili.com,按回车之后发生了什么?请从技术的角度描述,如浏览器、网络(UDP、TCP、HTTP等),以及服务器等各种参与对象上由此引发的一系列活动,请尽可能的涉及到所有的关键技术点。

    使用HTTP/3.0以前版本:
    1. 浏览器解析输入的URL,提取出协议(https://)、主机名(www.bilibili.com)和路径(/)等信息
    2. DNS解析域名获取IP
    3. TCP三次握手建立连接
    4. 浏览器发起HTTP请求
    5. 服务器处理请求并返回响应报文
    6. 浏览器渲染页面
    7. TCP四次挥手断开连接
    使用HTTP/3.0版本:
    1. 浏览器解析输入的URL,提取出协议(https://)、主机名(www.bilibili.com)和路径(/)等信息
    2. DNS解析域名获取IP
    3. 浏览器建立UDP连接到指定的服务器IP地址,并发送一个QUIC连接请求
    4. 浏览器发送HTTP/3.0请求到服务器
    5. 服务器处理请求并生成一个HTTP/3.0响应返回给浏览器
    6. 浏览器渲染页面

    浏览器解析输入的URL

    DNS域名解析

    DNS解析的过程就是寻找哪台机器上有你需要资源的过程,寻找的过程遵循就近原则。

    输入一个网址并按回车的时候浏览器会根据输入的URL去查找对应的IP,具体过程如下:

    (1)先查浏览器缓存,浏览器会保存一段时间内访问过的一些网址的DNS信息,不同浏览器保存的时常不等。

    (2)如果没有找到对应的记录,这个时候浏览器会尝试调用操作系统缓存来继续查找这个网址的对应DNS信息。

    (3)如果还是没找到对应的IP,那么接着会发送一个请求到路由器上,然后路由器在自己的路由器缓存上查找记录,路由器一般也存有DNS信息。

    (4)如果还是没有,这个请求就会被发送到ISP(注:Internet Service Provider,互联网服务提供商,就是网络运营商,中国电信中国移动等),ISP也会有相应的ISP DNS服务器,就是本地DNS服务器,请求的域名基本上都能在这里找得到。

    (5)如果还是没有的话, ISP的DNS服务器会将请求发向根域名服务器进行搜索。根域名服务器就是面向全球的顶级DNS服务器,共有13台逻辑上的服务器,从A到M命名,真正的实体服务器则有几百台,分布于全球各大洲。

    (6)如果到了这里还是找不到域名的对应信息,那只能说明一个问题:这个域名本来就不存在,它没有在网上正式注册过。或者域名过期了。

    这也就是为什么有时候打开一个新页面会有点慢,因为如果本地没什么缓存,查找域名的过程要这样递归地查询下去,查找完还要一层层的向上返回。

    域名结构为:三级域名.二级域名.一级域名。例如"mp3.baidu.com",域名先是解析出这是个.com的域名,然后跑到管理.com域的服务器上进行进一步查询,然后是.baidu,最后是mp3, 

    总结

            DNS根据域名查询IP地址的过程为:浏览器缓存 --> 操作系统缓存 --> 路由器缓存-->本地(ISP)域名服务器缓存 --> 根域名服务器。

  • 相关阅读:
    win10电脑不小心卸载的软件怎么恢复
    服了,头条4面:因为一个问题问题砍了我10万薪水
    beyond Compare连接 openWrt 和 VsCode
    设计模式之责任链模式
    04-树5 Root of AVL Tree
    c语言环形队列
    【深度学习】——损失函数(均方差损失和交叉熵损失)
    React-router-dom 6总结
    C++ - 使用标准库实现事件和委托,信号和槽机制
    .NET 邮件发送 SMTP邮件发送
  • 原文地址:https://blog.csdn.net/weixin_60053402/article/details/133134033