HTTP协议

参考:HTTP常见面试题

HTTP的基础知识

1.HTTP超文本传输协议
超文本指的是:它可以是文字、图片、视频等的混合体

2.五类状态码:

  • 1xx 类状态码属于提示信息,是协议处理中的一种中间状态,实际用到的比较少。
  • 2xx 类状态码表示服务器成功处理了客户端的请求
  • 3xx 类状态码表示客户端请求的资源发生了变动,需要客户端用新的 URL 重新发送请求获取资源,也就是重定向。浏览器会自动根据返回的url进行重定向操作。
    当返回304时,代表重定向到已存在的缓冲文件。
  • 4xx 类状态码表示客户端发送的报文有误,服务器无法处理,也就是错误码的含义。
  • 5xx 类状态码表示客户端请求报文正确,但是服务器处理时内部发生了错误,属于服务器端的错误码。

3.HTTP 常见(请求头部字段)字段有哪些?

  • Host:客户端发送请求时,用来指定服务器的域名。
  • Content-Length:body大小
  • Connection: Keep-Alive:表示保持长连接,即只要任意一端没有明确提出断开连接,则保持 TCP 连接状态。
  • Content-Type:数据格式。Accept:声明自己接收的数据格式
  • Content-Encoding:说明数据的压缩方法。Accept-Encoding 字段说明自己可以接受哪些压缩方法。

4.HTTP协议解决TCP 粘包问题:
HTTP 协议通过设置回车符、换行符作为 HTTP header 的边界,通过 Content-Length 字段作为 HTTP body 的边界,这两个方式都是为了解决“粘包”的问题。

5.GET 和 POST 有什么区别?

  • GET 请求的参数位置一般是写在 URL 中,URL 规定只能支持 ASCII,所以 GET 请求的参数只允许 ASCII 字符 ,而且浏览器会对 URL 的长度有限制(HTTP协议本身对 URL长度并没有做任何规定)。
  • POST 请求携带数据的位置一般是写在报文 body 中, body 中的数据可以是任意格式的数据,只要客户端与服务端协商好即可,而且浏览器不会对 body 大小做限制。

GET 请求可以带 body 吗?答:可以携带,但一般不携带。GET 请求可以带 body 吗?

6.强制缓存与协商缓存

  • 强制缓存:服务器的response中设置Cache-Control或Expires字段,来告诉浏览器此资源过多久才过期,浏览器可以缓存起来。当有缓存的时候,不会访问服务器。
  • 协商缓存:每次都问一下服务器,我这个缓存是否过期,没有过期,则将收到状态码304,然后直接使用已存在的缓冲文件。

协商缓存可以基于两种头部来实现:

  • 响应头部中的 Last-Modified:标示这个响应资源的最后修改时间;
    请求头部中的 If-Modified-Since:标示这个响应资源的最后修改时间;
  • 响应头部中 Etag:唯一标识响应资源;
    请求头部中的 If-None-Match:将请求头If-None-Match 值设置为 Etag 的值。服务器收到请求后进行比对,如果资源没有变化返回 304,如果资源变化了返回 200。

ETag 和 Last-Modified:

  • 第一种实现方式是基于时间实现的,第二种实现方式是基于一个唯一标识实现的,相对来说后者可以更加准确地判断文件内容是否被修改,避免由于时间篡改导致的不可靠问题。
  • ETag 和 Last-Modified同时存在时,Etag 的优先级更高
  • 协商缓存这两个字段都需要配合强制缓存中 Cache-control 字段来使用,只有在未能命中强制缓存的时候,才能发起带有协商缓存字段的请求。也是说只有在判断资源过期以后,才进行协商缓存。

7.HTTP 常见到版本有 HTTP/1.1,HTTP/2.0,HTTP/3.0,HTTPS
HTTP 由于是工作在应用层( OSI 第七层),则它下层可以随意变化,比如:

  • HTTPS:HTTPS 就是在 HTTP 与 TCP 层之间增加了 SSL/TLS 安全传输层;
  • HTTP/2.0:HTTP/1.1 和 HTTP/2.0 传输协议使用的是 TCP 协议,而到了 HTTP/3.0 传输协议改用了 UDP 协议。

8.HTTP/1.1 的性能:

  • 长连接:只要任意一端没有明确提出断开连接,则保持 TCP 连接状态。
  • 管道网络传输:可在同一个 TCP 连接里面,客户端可以发起多个请求,只要第一个请求发出去了,不必等其回来,就可以发第二个请求出去,可以减少整体的响应时间。【实际上 HTTP/1.1 管道化技术不是默认开启,而且浏览器基本都没有支持。大家知道有这个功能,但是没有被使用就行了。】

9.无状态和Cookie
无状态:协议对于事务处理没有记忆能力,后续处理需要前面的信息,则它必须重传,这样可能导致每次连接传送的数据量增大。
解决无状态问题:
Cookie:在客户端第一次请求后,服务器会下发一个装有客户信息的Cookie,后续客户端请求服务器的时候,带上Cookie,服务器就能认得了了

HTTPS

1.HTTP 与 HTTPS:因为 HTTP 传输的内容都是明文的,虽然在浏览器地址拦看不到 POST 提交的 body 数据,但是只要抓个包就都能看到了。
所以,要避免传输过程中数据被窃取,就要使用 HTTPS 协议,这样所有 HTTP 的数据都会被加密传输。

  • HTTPS 则解决 HTTP 不安全的缺陷,在 TCP 和 HTTP 网络层之间加入了 SSL/TLS 安全协议,使得报文能够加密传输。
  • HTTPS 在 TCP 三次握手之后,还需进行 SSL/TLS 的握手过程,才可进入加密报文传输。
  • 两者的默认端口不一样,HTTP 默认端口号是 80,HTTPS 默认端口号是 443。
  • HTTPS 协议需要向 CA(证书权威机构)申请数字证书,来保证服务器的身份是可信的。

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

  • 在通信建立前采用非对称加密的方式交换「会话秘钥」,后续就不再使用非对称加密。非对称加密使用两个密钥:公钥和私钥,公钥可以任意分发而私钥保密,解决了密钥交换问题但速度慢。
  • 在通信过程中全部使用对称加密的「会话秘钥」的方式加密明文数据。对称加密只使用一个密钥,运算速度快,密钥必须保密,无法做到安全的密钥交换。

3.非对称加密的公钥和私钥:
公钥和私钥:这两个密钥可以双向加解密的

  • 公钥加密,私钥解密。由于其他人没有私钥,所以无法查看传输的内容,这就保证了传输的内容不会外泄。但是消息还是会被冒充。
  • 私钥加密,公钥解密。由于其他人没有私钥,所以如果公钥可以解密出内容,就可以证明这个消息是来源于持有私钥身份的人。保证消息不被冒充,保证消息来自私钥持有人,但是消息可能外泄。

4.摘要算法(哈希函数)——防止内容篡改
摘要算法:发送方计算内容的哈希值,并将内容和哈希值传送到接收端,接收端重新计算内容的哈希值,然后查看哈希值是否发生变化,从而要确定是内容是否被篡改。
但是黑客可以通过同时修改内容和哈希值来欺骗接收方。为解决这个问题,我们需要使用私钥加密哈希值,然后公钥解密哈希值。只加密哈希值是为了减少加解密耗费的时间。

5.数字证书
问题:黑客可能给客户端发送自己的公钥,然后客户端使用此公钥加密的数据,就被黑客一览无遗。这可以通过数字证书解决。
数字证书:

  • 数字证书生成:首先 CA 会把持有者的公钥、用途、颁发者、有效时间等信息打成一个包,然后对这些信息进行 Hash 计算,得到一个 Hash 值;
    然后 CA 会使用自己的私钥将该 Hash 值加密,生成 Certificate Signature,也就是 CA 对证书做了签名;
    最后Certificate Signature、持有者的公钥、用途、颁发者、有效时间等信息一同组成数字证书;
  • 服务器会向客户端发送自己的数字证书。首先客户端会使用同样的 Hash 算法计算持有者的公钥、用途、颁发者、有效时间等信息组成的包,获取Hash 值 H1;
    通常浏览器和操作系统中集成了 CA 的公钥信息,浏览器收到证书后可以使用 CA 的公钥解密 Certificate Signature 内容,得到一个 Hash 值 H2 ;
    最后比较 H1 和 H2,如果值相同,则为可信赖的证书,否则则认为证书不可信。

证书链:CA公钥用于解密签名,为了验证CA公钥合法,就需要另一个公钥验证此CA公钥,这样就会形成无限循环。所以操作系统或浏览器中内置了很多可被信任的根证书,根证书中的公钥是被信任的。(参考:什么是 HTTPS 的证书信任链?自己给自己发行不行?

  • 首先操作系统或浏览器中内置了很多可被信任的根证书。当客户端收到了证书但证书的签发者不是根证书时,就会不断地向上请求中间证书,直到中间证书的签发者为根证书为止。然后利用根证书中的公钥去验证下层的中间证书,下层证书中的公钥去验证下下层的中间证书,不断往下,直到验证完成。
    所以如果浏览器或系统中的证书被病毒修改,那么就会导致我们信任不合法的证书,从而导致信息被获取。
  • 【问题】Root CA 为什么不直接颁发证书,而是要搞那么多中间层级呢?
    答:这是为了确保根证书的绝对安全性,将根证书隔离地越严格越好,不然根证书如果失守了,那么整个信任链都会有问题。(背下来吧,我也不懂为什么。)

6.SSL/TLS 协议基本流程:

  • 客户端向服务器索要并验证服务器的公钥。
  • 双方协商生产「会话秘钥」。
  • 双方采用「会话秘钥」进行加密通信。

前两步也就是 SSL/TLS 的建立过程,也就是 TLS 握手阶段。

TLS 的「握手阶段」涉及四次通信,使用不同的密钥交换算法,TLS 握手流程也会不一样的,现在常用的密钥交换算法有两种:RSA 算法 (opens new window)和 ECDHE 算法。

RSA 握手过程

总结:

  • 在 TLS 握手阶段会将TLS 证书(服务端的公钥)传递给客户端,而服务端的私钥则一直留在服务端,一定要确保私钥不能被窃取。
  • 在 RSA 密钥协商算法中,客户端会生成随机密钥,并使用服务端的公钥加密后再传给服务端。根据非对称加密算法,公钥加密的消息仅能通过私钥解密,这样服务端解密后,双方就得到了相同的密钥,再用它加密应用消息。

RSA的四次握手:

  • 1.客户端发送TLS 版本号、支持的密码套件列表,以及生成的随机数(Client Random),这个随机数会被服务端保留,它是生成对称加密密钥的材料之一。

  • 2.当服务端收到客户端的「Client Hello」消息后,会确认 TLS 版本号是否支持,和从密码套件列表中选择一个密码套件,以及生成随机数(Server Random)。
    然后发送「Server Certificate」给客户端,这个消息里含有数字证书。
    并且此次握手,服务端还发了「Server Hello Done」消息,目的是告诉客户端,我已经把该给你的东西都给你了,本次打招呼完毕。
    【注】上面的所有信息是是一次性发送给客户端的。

  • 3.客户端收到数字证书以后,会对证书按照前文所述进行验证。

    • 生成对称密钥:客户端就会生成一个新的随机数 (pre-master),此时就有了三个随机数,使用这三个随机数生成会话密钥(Master Secret),它是对称密钥,用于对后续的 HTTP 请求/响应的数据加解密。此时当前生成的随机数会用服务器给的RSA 公钥加密并通过「Client Key Exchange」消息传给服务端,然后服务端使用得到的三个随机数也可以生成会话密钥。【最后一个随机数是同非对称加密进行加密】
    • 通知使用对称密钥加密:客户端生成完「会话密钥」后,然后客户端发一个「Change Cipher Spec」,告诉服务端开始使用加密方式发送消息。
    • 确认加密通信可用:客户端再发一个「Encrypted Handshake Message(Finishd)」消息,把之前所有发送的数据做个摘要,再用会话密钥(master secret)加密一下,让服务器做个验证,验证加密通信「是否可用」和「之前握手信息是否有被中途篡改过」。
    • 【注】上面三个信息是同时发送的。
  • 4.服务器也是发「Change Cipher Spec」和「Encrypted Handshake Message」消息,如果双方都验证加密和解密没问题,那么握手正式完成。

RSA缺点:
使用 RSA 密钥协商算法的最大问题是不支持前向保密。【什么叫前向保密?】
因为客户端传递随机数(用于生成对称加密密钥的条件之一)给服务端时使用的是公钥加密的,服务端收到后,会用私钥解密得到随机数。所以一旦服务端的私钥泄漏了,过去被第三方截获的所有 TLS 通讯密文都会被破解。
为了解决这个问题,后面就出现了 ECDHE 密钥协商算法,我们现在大多数网站使用的正是 ECDHE 密钥协商算法

ECDHE 握手过程

1.客户端发送TLS 版本号、支持的密码套件列表,以及生成的随机数(Client Random),这个随机数会被服务端保留,它是生成对称加密密钥的材料之一。

2.当服务端收到客户端的「Client Hello」消息后,会确认 TLS 版本号是否支持,和从密码套件列表中选择一个密码套件,以及生成随机数(Server Random)。

  • 然后发送「Server Certificate」给客户端,这个消息里含有数字证书。
  • 选择椭圆曲线基点 G来生成私钥,然后使用私钥生成公钥,然后将公钥和G发送给客户端。【与RSA的不同处】
  • 为了保证这个椭圆曲线的公钥不被第三方篡改,服务端会用 RSA 签名算法给服务端的椭圆曲线公钥做个签名。【与RSA的不同处】
  • 服务端还发了「Server Hello Done」消息,目的是告诉客户端,我已经把该给你的东西都给你了,本次打招呼完毕。
  • 【注】上面的所有信息是是一次性发送给客户端的。

3.客户端收到数字证书以后,会对证书按照前文所述进行验证。

  • 客户端会生成一个随机数作为客户端椭圆曲线的私钥,然后再根据服务端前面给的信息,生成客户端的椭圆曲线公钥,然后用「Client Key Exchange」消息发给服务端。
    使用「客户端随机数 + 服务端随机数 + x(ECDHE 算法算出的共享密钥) 」生成会话密钥。【与RSA的不同处】
  • 通知使用对称密钥加密:客户端生成完「会话密钥」后,然后客户端发一个「Change Cipher Spec」,告诉服务端开始使用加密方式发送消息。
  • 确认加密通信可用:客户端再发一个「Encrypted Handshake Message(Finishd)」消息,把之前所有发送的数据做个摘要,再用会话密钥(master secret)加密一下,让服务器做个验证,验证加密通信「是否可用」和「之前握手信息是否有被中途篡改过」。
  • 【注】上面三个信息是同时发送的。

4.服务端也会有一个同样的操作,发「Change Cipher Spec」和「Encrypted Handshake Message」消息,

ECDHE使用临时密钥来保证即使私钥被泄露,攻击者也无法解密过去的通信内容(前向保密):每次建立新的会话时,都会生成一个新的临时密钥,而不使用长期有效的密钥对。临时密钥仅在当前会话中有效,不会被保存或传输到其他会话。

既然有 HTTP 协议,为什么还要有 RPC?

是先有的RPC,所以我们该问的不是既然有HTTP协议为什么要有RPC,而是为什么有RPC还要有HTTP协议。

  • RPC是远程方法调用,它使得调用远程方法和调用本地方法一样简单,而我觉得HTTP协议本质上就是一个远程调用的协议,所以为了实现“调用远程方法和调用本地方法一样简单”,只需要在HTTP协议上进行封装就可以了,如grpc底层使用的就是HTTP2。
    很多公司内部的rpc调用都是在HTTP2.0出现之前就用了,所以使用了各种公司内部的协议。HTTP2.0在HTTP1.1的基础上做了优化,性能可能比很多RPC协议都要好,但由于是这几年才出来的,很多公司内部的RPC协议都已经跑了好些年了,基于历史原因,一般也没必要去换了。

  • 刚开始HTTP的出现是为了统一浏览器与服务器之间的通信协议,即b/s架构,现在也用在c/s架构,这样做的好处是:服务器使用同一套协议向外提供服务,不管是网页版、安卓端、还是pc端,都可以使用服务器提供的同一套服务,即后端代码都是同一套,只是前端代码不同而已。

既然有 HTTP 协议,为什么还要有 WebSocket?

服务器主动发送给客户端的方法:

  • 使用 HTTP 不断轮询:如扫码登录,前端网页根本不知道用户扫没扫,于是不断去向后端服务器询问,看有没有人扫过这个码。一般一秒左右轮询一次,这就导致一秒左右的延迟。这里是前端通过一个循环来不断询问服务器。

  • 使用 HTTP 长轮询:如果我们的 HTTP 请求将超时设置的很大,比如 30 秒,在这 30 秒内只要服务器收到了扫码请求,就立马返回给客户端网页(无延迟,体验好)。如果超时,那就立马发起下一次请求。这里是后端通过一个循环来检测是否收到扫码信息。

  • WebSocket:HTTP是半双工的,WebSocket是全双工的。 在HTTP 请求里带上一些特殊的header 头,从而将协议转换为WebSocket,即通过两次 HTTP 握手,转换协议为WebSocket。
    转换以后的通信就和HTTP协议没有关系了。

对于像扫码登录这样的简单场景还能用用HTTP协议。但如果是网页游戏呢,游戏一般会有大量的数据需要从服务器主动推送到客户端,如在网页游戏里,怪物移动以及攻击玩家的行为是服务器逻辑产生的,对玩家产生的伤害等数据,都需要由服务器主动发送给客户端,客户端获得数据后展示对应的效果。这是就需要用到WebSocket。
WebSocket适用于需要服务器和客户端(浏览器)频繁交互的大部分场景,比如网页/小程序游戏,网页聊天室,以及一些类似飞书这样的网页协同办公软件。

posted @ 2022-12-04 22:45  好人~  阅读(104)  评论(0编辑  收藏  举报