计算机网络-HTTP修炼之道(基础篇)


1. HTTP是什么?

HTTP叫做超文本传输协议,它本质上是一个协议,如果要完全理解的话可能通过三个关键字进行解读

  • 超文本

什么叫超文本,首先要知道什么叫文本,在浏览网页的时候,有视频,图片,音频等等,但是这些在HTTP的规定中,他们是以文本的形式进行传输的,然而这些特殊数据从本质上就已经超越了文本的范围,最关键的还有一个称为超链接的数据类型,它能够从一个超文本文件跳转到另外一个超文本

总结:超文本从定义上来说就是超级文本,它是视频、图片等的混合体,超链接的出现使得超文本之间的跳转更加方便.

  • 传输

HTTP是一个双向协议,只要通信的机器之间遵循HTTP协议,就能够基于HTTP协议实现数据的传输

  • 协议

协议就是行为的规范,他规定了数据收发双方中如何处理这些数据,以什么样的形式解析数据。

HTTP是一个在计算机间传输数据的约定和规范的应用层协议。

2. HTTP常见的状态码有哪些?

状态码前缀 具体含义 常见的状态码有
1xx 提示信息,表示目前是协议处理的中间状态,需要后续的操作
2xx 成功,提交并且已经被处理了 200,204,206
3xx 重定向,资源位置发生变动,需要客户端重新发送请求 301,302,304
4xx 客户端有错误,请求报文存在错误,服务器无法处理 400 403 404
5xx 服务器错误,服务器在处理请求的时候发生了错误 500 501 502 503

[200 OK]:表示一切正常

[204 No Content]:表示一切正常,但没有返回数据过来

[206 Partial Content]:是应用于HTTP分块下载或者断点续传,表示响应返回的body数据不是资源的全部,而是其中的一部分,也是服务器处理成功的标志

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

[301 Moved Permenently]:表示的是永久重定向,说明请求的资源已经不存在了,需要改用新的URL再次访问

[302 Found]:表示临时重定向,说明请求的资源还是存在的,但是暂时需要用另外一个URL进行重新定向

[304 Modified]:不具有跳转的含义,表示资源未修改,请到缓存中取,用于缓存的控制

4xx是说客户端发送的报文有错误

[400 Bad Request]:表示客户端请求的报文是错误的

[403 Forbidden]:表示服务器禁止访问这个资源,并不是客户端的请求出错

[404 Not Found]:表示请求的资源在服务器上不存在

5xx是说服务端有问题

[500 Internal Server Error]:与400类似,是一个错误的笼统称呼

[501 Not Implemented]:表示客户端请求的功能还不支持

[502 Bad Gateway]:通常是服务器作为网关或者代理时返回的错误码,表示服务器自身的工作是正常的,访问后端服务器是异常的

[503 Service Unaviable]:表示当前服务器很忙,暂时无法响应客户端

3. HTTP的常见字段有哪些?

Host:用来指定服务器的域名

Content-length:表示本次请求返回去的数据长度,其主要目的是为了解决TCP的粘包/半包问题

这个方案是基于字节流的长度作为解决方案的

Connection:这个字段常用于客户端要求服务器使用HTTP长连接机制,当使用长连接的时候,就是Keep-Alive

它规定了:只要任意一端没有提出断开连接,就会复用这条TCP连接进行数据的传输

Content-Type:这个字段规定了用于服务器响应的时候,告诉客户端,本次数据是什么格式

客户端请求的时候,使用Accpet来告诉服务器,客户端能够接收什么样的数据

比如说有text/htmlapplication/json

Content-Encoding:表明数据的压缩方法,表示服务器返回的数据用了什么样的压缩格式

Accept-Encoding:表明自己可以接受哪些压缩方法

  • gzip
  • deflate

4. GET和POST有什么区别?

GET请求

GET的语义是说从服务器获取指定的资源,这个资源可以是静态的文件,页面,图片,视频等。GET请求的参数位置一般是写在URL中的,URL规定只能支持ASCII,所以GET请求的参数只允许ASCII字符,而且浏览器对URL的长度是有限制的

POST请求

POST的语义是根据请求的负荷(报文body)对指定的资源做出处理,具体的处理方式视资源的类型而不同,POST请求携带的数据一般都是写在body中的,而且浏览器对body的大小不会做出限制

实际上Get也能带Body,只是规范中不提倡,同时Post请求的url中也能提交参数。

5. GET和POST都是幂等的吗?

安全:请求方法不会破坏浏览器上的资源

幂等:多次执行相同的操作,结果都是相同的

  • Get方法是安全而且幂等的,因为它是只读操作,无论操作多少次,都不会对服务器上的数据造成影响

基于GET请求的幂等性,因为它是只读的操作,无论操作多少次,服务器上的数据都是安全的,所以可以对GET请求做缓存,这个缓存既可以缓存到代理服务器上,也可以缓存到浏览器本身

  • POST因为是新增或者提交数据,因此是不安全的,同时也不一定是幂等的,因为提交数据可能导致服务器资源的改变,而且多次提交就会多次创建资源

为了避免数据被窃取,就要使用HTTPS协议

6. HTTP缓存的实现方式?

强制缓存协商缓存

这两种缓存的对象都是那些每次请求都能得到一样的请求数据

7. 详细讲讲什么是强制缓存?协商缓存?

强制缓存:强制缓存指的是浏览器只要判断缓存没有过期,就可以继续使用,决定是否使用缓存在于浏览器这边

比如说我在项目开发的时候,有一次偶然看到HTTP的状态码中标注from disk cache

强缓存的实现原理是利用两个HTTP的响应头部Response Header字段实现的,他们用来标识资源过期的时间

分别是:

  • Cache-Control:表示的是相对过期时间,通过max-age进行存储
  • Expires:表示的是绝对过期时间

当绝对过期时间和相对过期时间都存在的时候,将以相对过期时间为准

当浏览器第一次访问服务器,发起请求之后,服务器返回响应的时候在头部加上Cache-Control的字段。

在之后的请求中,浏览器发出请求后会根据请求资源的时间域Cache-Control的大小关系来判断缓存是否过期,如果缓存过期,那么就会服务器发起请求,然后服务器重新写入Cache-Control

协商缓存:这个过程可以这样进行描述,浏览器服务端协商之后根据结果来判断是否使用缓存

使用协商缓存的原理是根据服务端返回来的响应码304确定的

协商缓存的实现原理比较复杂,我简单概述为通过时间进行校验和通过唯一标识进行校验

首先第一种是基于时间进行校验,如果是我来设计这个HTTP缓存的协商缓存的话,思路就是这样的:

首先,服务端会有每个资源被最后修改后的时间信息,本地缓存的资源有一个过期的时间信息

那么什么时候缓存会失效?就是服务端的资源和本地缓存的资源不一样了,就是缓存不一致,失效了

那么这时候就要更新缓存,那么问题就来了,如何判断服务端的资源和本地缓存的资源不一样了呢?

可以充分利用HTTP的首部,通过首部的信息进行通信

首先响应资源的服务器会给响应头打上一个时间,这个时间是指这个资源被最后修改的时间,叫做Last-Modified,同时的,还有一个字段是If-Modified-Since,这个字段标志的是上次缓存的资源的最终更新时间,也就是上一次缓存资源的时候获取的Last-Modified

只用一个Last-Modified不就行了吗?为什么还要用多一个If-Modified-Since呢?

我们先来思考第一个问题,只用Last-Modified可以吗?

答案是肯定的,只要每次请求都带上这个Last-Modified,然后服务器检查服务器上的资源的最后修改时间,然后执行比较即可。

而第二个问题就涉及到HTTP的设计原理了,首先我们要知道像If-xxx这样的字段,它属于附带条件请求,它存在的意义是为了图灵机在处理这些协议的时候,更加方便地编写逻辑,服务端在接收到这样的请求字段后,还有判断条件为真的时候,才会执行请求,否则将不会执行请求。

那么在这里的话对第一种方案的执行流程做一个简述:

首先在请求的时候,在请求头带上If-Modified-Since,发送给服务端,服务端拿着这个值与它本地的这个资源的最后修改时间进行比较,如果IF-Modified-Since与服务器上的最后修改时间要晚,那么就不需要修改,直接返回状态码304,让它走缓存即可

否则的话,就是说本地的资源的修改时间比服务器的资源修改时间要早,那么这时候就要重新请求资源,在这个过程中,返回一个Last-Modified,然后本地的资源修改最后修改时间和If-Modified-Since

第二种方案是基于标签号eTagIf-None-Match实现的

核心思路:只有当eTagIf-None-Match的值不一致的时候才执行请求,否则不执行

If-None-Match代表着此次请求是附加条件请求。

eTag:代表的是资源的标识号

当资源过期的时候,它会先去浏览器的响应头找,发现有eTag,于是在请求头上携带If-None-Match

注意,这个eTag是从本地缓存中来的哈,然后它会去请求服务器的eTag,如果它发现eTag不存在或者相同的,那么就直接走304,否则的话再次请求数据

8. 强制缓存和协商缓存有什么区别?

首先,强制缓存是基于浏览器实现的,控制权全部在浏览器中,如果浏览器判断缓存命中,那么直接走缓存,如果它判断缓存不命中,直接则会直接走服务器请求,服务器返回最新资源。

而协商缓存则是在走服务器请求这一步,减少了网络的通信量,它通过时间或者标识号的这两种机制,判断缓存中的内容是否真的发生了变化,如果真的发生了变化才重新从网络上加载数据,否则还是走本地缓存。

9. 讲讲HTTP的缓存工作原理

HTTP的缓存工作原理是基于强缓存+协商缓存的,这种实现能够尽可能的减少网络通信量。

首先,当浏览器要发出一个请求时,他会先检查字段,分别是Cache-Control,这个字段在缓存工作时设置为max-age=???,然后在通过比较当前的时间和请求头中的Date+max-age值的大小,如果小于了,直接走缓存,否则的话视为缓存不命中。

当缓存不命中的时候,开始请求服务器,首先它会发现响应头中有eTag,于是将eTag的值赋值给If-None-Match,如果这个If-None-Match在服务端找不到或者在服务端的资源能够对应上,这时候直接返回304,告诉浏览器走缓存,否则携带上这个eTag进行响应。

如果没有eTag这个字段,它会在响应头中找一个叫做Last-Modified的字段,然后通过赋值If-Modified-Since,发起新新的请求,服务端就检查这个资源的Last-Modified,然后这时候会有两种情况

  • 客户端的修改时间比服务端的修改时间要晚,因此直接走缓存,304
  • 客户端的修改时间比服务端的修改时间要早,因此重新发送数据,200

然后在返回新响应的时候,带上这个Last-Modified

还有一种情况是强缓存仅基于expire实现,它记录的是绝对时间,但是通常由于服务端时钟和客户端时钟不一致,而不使用,精度较低

10. HTTP/1.1了解过吗?说说优点和缺点?

1. HTTP的优点

  • 简单

HTTP报文段的格式就是head+Body,头部信息也是基于key-value实现的,因此简单

  • 灵活,容易扩展

HTTP协议中规定的URIURL等组成部分并没有固定,允许开发人员自行定义和扩充,因此功能上比较容易扩充

其底层的实现是可插拔式的,下层可以修改为TCP或者UDP,而且用户无感知,或者增加TSL/SSL等安全认证机制

  • 跨平台:各种设备都能够使用HTTP协议进行网络的通信

2. HTTP/1.1无状态的双刃剑问题

HTTP协议是无状态的,无状态的好处是能够减少维护这次连接上的各种复杂的状态信息,减轻了CPU和内存的负担,使得这些资源更加专注于为用户提供服务

坏处则是其因为无法存储当前的状态信息,当需要完成一系列有关联性的操作的时候,需要添加附加信息

比如说使用Cookie技术完成记录当前的用户的token,如果cookie生效,就证明用户完成过登录的操作,这样的话就能够使得关联性的操作被记录下来

3. HTTP/1.1明文传输的双刃剑问题

明文传输的好处是程序容易调试,仅仅使用浏览器就能够对请求的信息进行分析

但是坏处是导致用户的信息泄漏,在复杂的网络中进行传输的时候,会经过错综复杂的网络结构,一旦被别有用心之人截获,就会导致非常严重的后果,问题的解决具有一定成本,比如引入复杂的加密算法,如TSL/SSL等机制,保护用户的信息,这增加了双方的通信量以及协议的复杂程度,因为还需要对加密的数据进行编码和解码

HTTP/1.1最严重的问题就是不安全,然而这个问题可以通过SSL/TSL的方式进行解决

4. HTTP/1.1的长连接是什么原理?

HTTP/1.1之前使用的是HTTP/1.0,在早期的1.0版本中,有一个非常严重的性能问题,就是它使用的是短连接,所谓短连接就是一次TCP的连接与释放对应一次HTTP的请求和响应,在高并发的情况下,会导致大量的TCP的三报文握手和四报文挥手,十分浪费,因为光是TCP报文的首部就要占20个字节,更不用说底层的IP头的加装额外消耗

于是HTTP/1.1提出了长连接,这个机制使得连接可复用,只要对等端没有任何一端提出连接的断开,就会一直复用这条连接。

5. 管道网络传输是什么?

长连接使得管道网络传输成为了可能,所谓管道网络传输是说,原本的请求-响应模型是阻塞式的,比如说第一个请求发出了,必须要等到第一个响应回来才能发第二个请求

而管道网络传输规定:可以一次性发送多个请求出去,但是接收响应必须按照请求的顺序进行接收。

然而HTTP/1.1是没有默认开启这个功能的

短连接因为单次连接的缘故,无法实现管道网络传输

6. 管道网络传输会带来什么问题?

这个问题叫做队头阻塞问题,严重时可能导致请求大量丢失和服务宕机

这个问题是这样发生,当第一个请求发送出去之后,由于I/O或者其他原因很长时间都没有收到响应,但是这时候后面的请求又都在排队,从而导致后面的请求无法得到响应,客户端陷入阻塞状态

这种会大大降低吞吐量,比如说请求2的完成只需要1ms,而请求1需要10s,等待的时间远远大于执行任务的时间。

HTTP/1.1性能比较差

11. HTTPS与HTTP有什么区别?

这个问题我认为可以从连接的建立过程传输的信息安全性默认端口通信基础来回答

  • HTTP的建立是依托于TCP连接的,因此在连接建立的过程只需要完成三次握手即可,而HTTPS因为在HTTP和TCP之间引入了SSL/TSL协议,因此在完成三次握手之后,还需要完成SSL/TSL握手
  • HTTP的报文传输是明文传输的,而HTTPS的报文传输是经过加密之后的
  • HTTP的默认端口是80,HTTPS的默认端口是443
  • HTTPS在通信的时候需要有对应的CA来证明服务器是可信的

12. HTTPS具体解决了HTTP的哪些问题?

首先我们来看看HTTP存在的安全问题:

  • 数据被窃听的风险:由于目前大部分的数据通信都是基于公开信道的,因此如果有技术人员截取数据,能够能够窃取到用户的信息
  • 数据被篡改的风险:同样的,由于公开信道以及报文的明文传输的缘故,技术人员可以通过伪造首部等信息,给对方一些假的数据,混淆视听
  • 冒充的风险:恶意网站通过伪造网站外观等手段混淆视听,服务器没有经过认证

那么HTTPS引入了TSL/SSL就是为了解决这些问题

  • 信息加密手段:交互信息在没有密钥的情况下无法解密
  • 校验机制:通过杂凑函数等手段,校验数据是否被篡改过,篡改过则直接丢弃数据报
  • 身份机制:通过CA机构颁发的证书证明服务器是可信的

13. HTTPS是如何解决这些问题的?

1. 混合加密算法

所谓混合加密算法,就是对称加密算法和非对称加密算法混合使用的一种算法,具体描述如下:

核心思路:对密钥执行的是非对称加密算法,对数据执行的是对称加密算法

其流程如下:

  • 首先是客户端和服务端双方执行密钥交换,在这个过程中执行的非对称加密算法,算法是这样的,首先使用公钥,对共享密钥进行加密,然后通过网络进行传输,然后当对等端接收到这个共享密钥后,用私钥进行解密,然后就得到了共享密钥
  • 得到了共享密钥后,之后的所有数据传输都是基于这个共享密钥了。

这样做的好处就是提高了加密和解密的效率,使用公钥和私钥的这种加密算法是非常慢的而如果使用共享密钥的方式就能够非常快的进行计算,因此这种方式提高了效率

2. 摘要算法

为了保证传输的内容没有被篡改过,我们希望有一种快速算法,能够对发送的数据内容进行校验。

摘要算法,也称为杂凑算法,它本质上就是一个映射,它能够将不等长的输入处理为等长的输出

这个函数具有以下的特点

  • 抗碰撞性:对于不同的输出得到相同的输入概率极低
  • 单向性:通过输入得到输出非常简单,但是想要通过输出得到输入在计算上不可行
  • 高灵敏性:不同的输入导致不同的输出,哪怕是只有一个比特位的不同,都会导致结果的不同

根据高灵敏性,就可以利用这个特性来做篡改检测

我们需要针对内容计算出一个指纹,然后将这个指纹一起传输给对方,对方拿到后,分离指纹和数据,然后对数据进行杂凑计算,判断指纹和计算出来的结果是否一致就可以知道是否被别人篡改过

注意,这个算法并不能解决数据造假的问题,如果存在中间人截获了杂凑函数的计算方式,那么他完全可以按照杂凑函数的规则,计算出一个合理的数据包进行使用

3. 加密算法的骚操作

  • 公钥加密,私钥解密:这个目的是为了内容传输的安全,只有持有私钥的人才能看懂我发出的数据
  • 私钥加密,公钥加密:因为私钥只有我知道,如果公钥能够解出数据,那么就证明数据的来源是私钥持有方,可以做一个身份的校验

实际上我们常说的数字签名算法就是基于此,只不过不是对数据内容进行加密,而是对内容哈希值进行加密。

4. 数字证书认证

  • 通过哈希算法可以保证消息的安全传输
  • 通过哈希算法可以保证消息发送方的身份

上述算法的一个巨大漏洞是:中间人完全可以伪造一对公钥和私钥

比如说,A向B获取公钥,这时候B发送的公钥被截获了,给的是中间人C的公钥,然后A根据公钥生成了自己的私钥,那么当A向中间人发送数据的时候,就能够解出数据是什么了,而B是完全不知道A的数据是什么的

上述的问题在于:没有对公钥进行校验,只要按照公钥的相关规则以及私钥的生成算法

就可以伪造成自己是合法一方。

解决思路:引入第三方权威机构,将个人信息+公钥+数字签名打包成一个数字证书

当需要使用公钥的时候,它会首先将这个数字证书使用CA的公钥进行解密,然后发现确实是CA颁发的数字证书,而且个人信息和数字签名都能对得上,这时候它就知道,公钥肯定是正确的公钥,于是放心使用

想要伪造一份数字签名能行吗?计算上不可行,因为数字证书的生成基于私钥计算的,而这个私钥只有CA机构知道是多少

14. HTTPS是如何建立的?

由于引入了TLS/SSL算法,因此在TCP的三报文握手之后,还要执行身份验证等操作

  • 客户端向服务器索要公钥并且验证公钥的身份
  • 双方协商生产共享密钥
  • 之后都使用共享密钥进行数据的传输

TLS的握手过程包含了四次通信过程

  • ClientHello:客户端向服务器发起加密通信请求

(1) 客户端支持的TLS版本号

(2) 客户端支持生产的随机数,后面用于生成会话密钥的条件之一

(3) 客户端支持的密码套件,如RSA加密算法

  • ServerHello

(1) 确定TLS版本号

(2) 服务器生产的随机数,生产会话密钥的条件

(3) 确认的密码套件

(4) 服务器的数字证书

  • 客户端回应

客户端收到数字证书后,通过浏览器等软件提供的CA公钥,确认服务器的数字证书的真实性

如果数字证书没有问题,就从中取出公钥,然后用其加密报文

(1) 一个随机数,这个随机数通过公钥进行加密

(2) 加密算法改变通知,之后都用共享密钥进行通信

(3) 客户端握手结束通知,表示客户端的握手阶段已经结束了,同时将之前的所有内容的发生数据做个摘要,用来给服务端进行验证

此时,一共生产了三个随机数,然后通过这三个随机数,其中第三个随机数是通过公钥加密的,最终生产出了共享密钥

  • 服务器的最后响应

(1) 加密通信算法改变通知,表示随后的信息都将用「会话秘钥」加密通信。

(2) 服务器握手结束通知,表示服务器的握手阶段已经结束。这一项同时把之前所有内容的发生的数据做个摘要,用来供客户端校验。

至此,整个 TLS 的握手阶段全部结束。接下来,客户端与服务器进入加密通信,就完全是使用普通的 HTTP 协议,只不过用「会话秘钥」加密内容。

15. HTTPS的应用是如何保证完整性的?

TLS的实现上分为握手协议和记录协议

  • 首先,消息被分割成多个较短的片段,然后分别对每个片段进行压缩。
  • 接下来,经过压缩的片段会被加上消息认证码(MAC 值,这个是通过哈希算法生成的),这是为了保证完整性,并进行数据的认证。通过附加消息认证码的 MAC 值,可以识别出篡改。与此同时,为了防止重放攻击,在计算消息认证码时,还加上了片段的编码。
  • 再接下来,经过压缩的片段再加上消息认证码会一起通过对称密码进行加密。
  • 最后,上述经过加密的数据再加上由数据类型、版本号、压缩后的长度组成的报头就是最终的报文数据。

记录协议完成后,最终的报文数据将传递到传输控制协议 (TCP) 层进行传输。

16. HTTPS一定安全可靠吗?

这个问题实际上考察的是中间人攻击的问题

首先我们在之前,已经讨论到了数字证书的问题了,但是这个数字证书只要具有一定的资质就可以申请

于是,如果中间人具有一份合法的数字证书,或者说它窃取了别人的数字证书,那么它也可以发起攻击

攻击的具体原因,首先客户端会向服务器发起请求,但是被一个假基站转发到了一个中间人服务器,然而这个中间人服务器使用了一份别人的数字证书,而这份数字证书是有效的,于是客户端完成了4次TLS握手,然后建立起通信,在这个过程中,中间人服务器同时向真正的服务器发起通信。

那么这样就很微妙了,中间人可以解开所有的数据,甚至能够篡改有关的数据,这样的话就会导致数据泄漏。

中间人服务器与客户端在 TLS 握手过程中,实际上发送了自己伪造的证书给浏览器,而这个伪造的证书是能被浏览器(客户端)识别出是非法的,于是就会提醒用户该证书存在问题。

如果用户执意点击「继续浏览此网站」,相当于用户接受了中间人伪造的证书,那么后续整个 HTTPS 通信都能被中间人监听了。

所以,这其实并不能说 HTTPS 不够安全,毕竟浏览器都已经提示证书有问题了,如果用户坚决要访问,那不能怪 HTTPS ,得怪自己手贱。

另外,如果你的电脑中毒了,被恶意导入了中间人的根证书,那么在验证中间人的证书的时候,由于你操作系统信任了中间人的根证书,那么等同于中间人的证书是合法的,这种情况下,浏览器是不会弹出证书存在问题的风险提醒的。

这其实也不关 HTTPS 的事情,是你电脑中毒了才导致 HTTPS 数据被中间人劫持的。

所以,HTTPS 协议本身到目前为止还是没有任何漏洞的,即使你成功进行中间人攻击,本质上是利用了客户端漏洞(用户点击继续访问或者被恶意导入伪造的根证书),并不是 HTTPS 不够安全

17. 为什么抓包工具能够截取HTTPS数据

很多抓包工具 之所以可以明文看到 HTTPS 数据,工作原理与中间人一致的。

对于 HTTPS 连接来说,中间人要满足以下两点,才能实现真正的明文代理:

  1. 中间人,作为客户端与真实服务端建立连接这一步不会有问题,因为服务端不会校验客户端的身份;
  2. 中间人,作为服务端与真实客户端建立连接,这里会有客户端信任服务端的问题,也就是服务端必须有对应域名的私钥;

中间人要拿到私钥只能通过如下方式:

  1. 去网站服务端拿到私钥;
  2. 去CA处拿域名签发私钥;
  3. 自己签发证书,切要被浏览器信任;

不用解释,抓包工具只能使用第三种方式取得中间人的身份。

使用抓包工具进行 HTTPS 抓包的时候,需要在客户端安装 Fiddler 的根证书,这里实际上起认证中心(CA)的作用。

抓包工具能够抓包的关键是客户端会往系统受信任的根证书列表中导入抓包工具生成的证书,而这个证书会被浏览器信任,也就是抓包工具给自己创建了一个认证中心 CA,客户端拿着中间人签发的证书去中间人自己的 CA 去认证,当然认为这个证书是有效的。

18. 如何避免被中间人抓取数据?

我们要保证自己电脑的安全,不要被病毒乘虚而入,而且也不要点击任何证书非法的网站,这样 HTTPS 数据就不会被中间人截取到了。

当然,我们还可以通过 HTTPS 双向认证来避免这种问题。

一般我们的 HTTPS 是单向认证,客户端只会验证了服务端的身份,但是服务端并不会验证客户端的身份。

如果用了双向认证方式,不仅客户端会验证服务端的身份,而且服务端也会验证客户端的身份。服务端一旦验证到请求自己的客户端为不可信任的,服务端就拒绝继续通信,客户端如果发现服务端为不可信任的,那么也中止通信。

19. HTTP/1.1 相比 HTTP/1.0 提高了什么性能?

这个问题我打算从HTTP/1.0HTTP/1.1的区别进行描述

首先HTTP/1.0的最重要的特征就是短连接,也就是一次请求与响应就对应一次TCP的连接与释放

因此在大量请求到来的时候,性能就会严重下降。

于是HTTP/1.1引入了长连接,这种机制就是说,只要请求的收发双方没有一方明确提出要断开连接,那么底层就会保留这条TCP连接,减少了TCP连接的三报文握手和四报文挥手的巨大开销

同时,由于多个请求-响应复用同一条TCP连接,这使得管道网络传输成为可能,也就是说,这时候客户端发送请求时,不必等待前一个请求的响应到达即可发送请求。

但是同时HTTP/1.1具有相当一部分缺点

  • dataheader没有做压缩,而只能对body做压缩,因此当header的体积过大时,将导致网络I/O传输性能下降
  • 将导致队头阻塞问题,虽然使用管道网络传输能够异步发送请求,但是接收响应却是同步的,在这种情况下,如果前面的响应没有发回来,然后后面的请求将一直无法接收到响应
  • 没有提供解决队头阻塞的优先级机制
  • 冗长的首部将导致传输浪费
  • 请求只能从客户端开始,服务器只能够被动响应,服务端无法主动推送服务

20. HTTP/2 做了什么优化?

关于这个问题,我想应该要从两个方面进行回答,第一个方面是安全方面

HTTP/2引入了TLS1.2+,也就是基于HTTPS的机制,使得HTTP/2的传输变得更加安全了

第二个方面是性能方面,性能方面做了较大的改动:

首先我们先来看看HTTP/1.1中各个痛点

  • HTTP/1.1的数据传输低效

它的传输低效主要存在于两点,第一,它的头部没有经过压缩,如果发送的请求/响应的头部冗长,而每次都发送这些请求的话,导致导致大量冗余信息在网络中传输

对于这个痛点,HTTP/2提供的解决方案是,客户端和服务端双方维护一张头信息表,这张表在初始化就被静态写入一些常用的头部字段,当需要使用到这些头部字段(以key-value的形式存储)的时候,就在数据包的头部中封装一个字段header:1 xxxx,其中1代表着这个头部字段在头信息表存在,xxx代表的是索引号,当第一个字段为0的时候,代表不存在,然后后边的这些xxx就是实际上的头部字段,然后传输到对等端的时候,会将这个字段记录到动态表中,方便下次使用

第二个低效的原因在于频繁的编码操作,HTTP/1.1的数据传输是基于文本传输的,而计算机硬件无法读取纯文本格式,只能够读取二进制的数据,在接收数据的时候,还需要将文本格式的数据解析为二进制的数据。

这就带来了严重的空间浪费了,比如说要传输的是200

  • HTTP/1.1中的形式是”2””0””0”,然后以二进制编码的方式就是三个字节
  • 然而在HTTP/2中由于全面采用了二进制编码格式,因此200就表示为1000 1000,节省了两个字节

全面采用了二进制,节省了传输所需要的编码量

  • 存在队头阻塞问题

这个队头阻塞问题指的是当响应迟迟没有到来的时候,后续的请求就无法得到响应

那么它是怎么解决的呢?

它引入了Stream流的概念,我们首先来理解为什么存在队头阻塞现象,这是因为如果不按请求顺序进行响应,就有可能将A的响应给到B,将B的响应给A,造成错乱

那么我们为什么不给这响应包加上一个控制字段,让这些包能够标识出来谁是谁的响应呢?

这就是Stream机制提出的一个核心思路,它在同一个TCP连接中存在多个Strteam,这多个Stream就代表着同一个请求-响应,他们通过在标识StreamId对这些Stream进行标识,从而确保对应的请求能够收到对应的请求

如果是这样的话,我们假设响应是乱序返回的,比如请求1、请求2发送,然后响应2、响应1返回,因为存在StreamId,接收方就能够通过不同Id标识拿到自己想要的数据包

通过Stream流的并发传输,提高了性能

  • 只能是客户端主动请求响应,而无法服务器主动推送

假设客户端向服务器等待服务器的长IO操作,只要等待它的长IO操作完成后才能返回响应,那么在这期间只能干等着,如果服务端能够主动推送响应,那么可以这样操作:

等服务器接收到客户端的请求,先临时告知客户端服务器正在操作,然后客户端就可以干自己的事情去了

然后当服务器做完自己的事情了,再向客户端推送,这样的话就可以提供用户的使用体验

HTTP/2有什么缺陷

它同样存在队头阻塞的问题,只不过它的队头阻塞问题是因为滑动窗口的限制而产生的,也就是队头阻塞产生在TCP层面,先来讲讲它是怎么产生的

我们假设发送双方维护了一个窗口,然后A同时发送了P1/P2/P3/p4/p5然后等待ACK,然后B能够接收P1/P2

P1,但是P2丢掉了,于是它只能够等待P2的超时重传,那么这期间,如果A再发送P3/P4/P5,都不能够再次接收

当考虑每个包大小一样的话,此时B应该能接收到P2/P3,但是后续的P4/P5均会被阻塞发送,即使到了也会被丢弃,因此此时P4/P5的响应就被搁置了,变成了队头阻塞

21. HTTP/3 做了哪些优化?

既然谈到了优化,那么肯定就是解决HTTP/2所没有解决的问题,HTTP/2没有解决TCP层面的队头阻塞问题

但是这个问题是致命的,因为发送窗口和接收窗口不可能无限制大小,而且为了可靠传输,必须保证数据包的有序接收,因此一个思路就是,修改底层的实现,改为UDP实现

首先要解决的问题是:之前提供的HTTP协议都是TCP的可靠传输,那么我们必须基于UDP实现可靠传输

核心就是上图中的QUIC协议

QUIC协议也有类似的HTTP/2中的Stream的机制,也就是说一条连接中有也有多个Stream,但是每一个Stream并不公用一个窗口,而是有各自的滑动窗口,因此如果流中的包丢失了,只会导致这个请求-响应被阻塞,其他的不被阻塞,这就解决了队头问题

与此同时,QUIC协议还基于UDP协议提高了性能

更快的连接建立

对比与HTTP/1.0、HTTP/1.1的实现,由于TCP+TLS的实现基于内核的TCP传输层和openssl库实现的,因此难以解耦,而QUIC的思路是:在完成连接握手的时候,在帧的首部携带上TLS认证信息,这个过程主要是为了确认双方的连接ID

甚至,在第二次连接的时候,应用数据包可以和 QUIC 握手信息(连接信息 + TLS 信息)一起发送,达到 0-RTT 的效果。

连接的迁移

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

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

所以, QUIC 是一个在 UDP 之上的 TCP + TLS + HTTP/2 的多路复用的协议。

QUIC 是新协议,对于很多网络设备,根本不知道什么是 QUIC,只会当做 UDP,这样会出现新的问题,因为有的网络设备是会丢掉 UDP 包的,而 QUIC 是基于 UDP 实现的,那么如果网络设备无法识别这个是 QUIC 包,那么就会当作 UDP包,然后被丢弃。


文章作者: 穿山甲
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 穿山甲 !
  目录