HTTP模块复习

184 阅读18分钟

本次复习模块是 - HTTP

本次要复习的内容是

  • TCP 三次握手,四次挥手
  • HTTP 几个版本的区别
  • HTTP 常见状态码
  • HTTP 常见 header
  • HTTPS
  • Cookie 是什么
  • 浏览器同源策略
  • 跨域怎么处理
  • 浏览器缓存策略:强制缓存,协商缓存

推荐一个资料:《小林coding》

一个完整的请求都经历了什么?

1. 构建请求 - 浏览器构建请求行信息,包含了请求方法、请求 URI和 HTTP 协议版本。 2. 查找缓存

  • ① DNS缓存:
    • 想要获取服务器IP还需要经过一步DNS解析
    • DNS层层缓存可以帮助我们快速解析找到目标服务器
  • ② 浏览器缓存:
    • 浏览器会将已请求过的资源文件在本地保存一份副本,当再次请求时若存在缓存文件,则不再执行下述步骤,直接返回缓存文件,这时候请求的返回状态码是304
  1. 准备 IP 地址和端口
    • 为TCP/IP协议请求准备IP地址(DNS解析-查找缓存阶段)与该进程端口号
  2. 等待 TCP 队列
    • 将请求事件插入至TCP队列,等待TCP队列轮训执行
    • 浏览器同一时间最多可建立 6 个 TCP 连接,队列长度<=6时,即可建立连接
  3. 建立 TCP 连接
    • TCP连接进入准备阶段。与目标服务器三次握手,为后续数据传输做好准备
  4. 发送 HTTP 请求
    • HTTP请求包含三部分:请求行(构建请求阶段)、请求头、请求
  5. 返回请求
    • 服务器端接收到客户端的请求,将作出处理并返回相应数据,包含响应行、响应头、响应体
  6. 断开连接
    • 若非长连接,TCP将断开与服务端的连接
    • 持久连接可复用信道(keep-alive/webSocker),减少请求握手、挥手过程

以上就是HTTP连接全过程

从输入URL到页面展示这中间发生了什么

一个完整的请求都经历了什么?可以进一步引申为:从输入URL到页面展示这中间发生了什么

当我们经历网络请求过程,从服务器端获取到了所访问的页面文件后就开始了浏览器解析渲染页面的动作;浏览器拿到响应文本后,解析HTML代码,请求js,css等资源,最后进行页面渲染,呈现给用户;

  1. 根据HTML文件解析出DOM树;
  2. 根据CSS解析出 CSSOM 树(CSS规则树)
  3. 将 DOM树和 CSSOM树合并,构建Render树(渲染树)
  4. 根据Render树进行节点信息计算(Layout–重排,回流)
  5. 根据计算好的信息绘制整个页面(Paint–重绘)

什么情况会导致重排?什么情况会导致重绘?

  • 重排:当某个dom节点的坐标发生了改变,导致浏览器需要重新进行布局的时候
  • 重绘:页面的样式发生了改变,但是没有改变他的位置、大小

TCP 三次握手,四次挥手

简单来说,握手就是 TCP 连接的一个过程;挥手就是告别嘛(我挥一挥衣袖,不带走一片云彩--- 再别康桥),挥手就是TCP 断开连接的过程

三次握手 过程

TCP 是面向连接的协议,所以使用 TCP 前必须先建立连接,而建立连接是通过三次握手来进行的。三次握手的过程如下图: 三次握手

为什么需要三次握手,而不是两次或者四次五次?

TCP 建立连接时,通过三次握手:

  1. 能防止历史连接的建立
  2. 能减少双方不必要的资源开销
  3. 能帮助双方同步初始化序列号

TCP 四次挥手

TCP 四次挥手的过程如下: image.png

为什么需要 四次挥手?

为什么 TCP 挥手需要四次呢? 服务器收到客户端的 FIN 报文时,内核会马上回一个 ACK 应答报文,但是服务端应用程序可能还有数据要发送,所以并不能马上发送 FIN 报文,而是将发送 FIN 报文的控制权交给服务端应用程序:

image.png

  • 如果服务端应用程序有数据要发送的话,就发完数据后,才调用关闭连接的函数;
  • 如果服务端应用程序没有数据要发送的话,可以直接调用关闭连接的函数,

从上面过程可知,是否要发送第三次挥手的控制权不在内核,而是在被动关闭方(上图的服务端)的应用程序,因为应用程序可能还有数据要发送,由应用程序决定什么时候调用关闭连接的函数,当调用了关闭连接的函数,内核就会发送 FIN 报文了, 所以服务端的 ACK 和 FIN 一般都会分开发送。

HTTP 几个版本的区别

到目前为止,HTTP 常见到版本有 HTTP/1.1,HTTP/2.0,HTTP/3.0,不同版本的 HTTP 特性是不一样的。

http1.1

优点

1. 长连接
  • 减少了 TCP 连接的重复建立和断开所造成的额外开销,减轻了服务器端的负载
  • 长连接的特点是,只要任意一端没有明确提出断开连接,则保持 TCP 连接状态
  • 当然,如果某个 HTTP 长连接超过一定时间没有任何数据交互,服务端就会主动断开这个连接
2.简单

HTTP 基本的报文格式就是 header + body,头部信息也是 key-value 简单文本的形式,易于理解

3.易拓展

HTTP 协议里的各类请求方法、URI/URL、状态码、头字段等每个组成要求都没有被固定死,都允许开发人员自定义和扩充。

缺点

HTTP 协议里有优缺点一体的双刃剑,分别是「无状态、明文传输」,同时还有一大缺点「不安全」。

无状态

无状态是优点也是缺点:

  • 优点:
    • 无状态的好处是服务器不需要额外的资源来记录状态信息,可以有更多的资源进行其他的业务处理。
  • 缺点:
    • 因为无状态,那么它在完成有关联性的操作时会非常麻烦,最常见的解决方法就是 Cookie 技术
队头阻塞
  • HTTP/1.1 的实现是基于请求-响应模型的。
  • 同一个连接中,HTTP 完成一个事务(请求与响应),才能处理下一个事务,也就是说在发出请求等待响应的过程中,是没办法做其他事情的,如果响应迟迟不来,那么后续的请求是无法发送的
明文传输

明文意味着在传输过程中的信息,是可方便阅读的,为我们的工作调试带来了很大的便捷,缺点就是所有的信息都是赤裸裸的裸奔状态,很容易就能被窃取

http2.0

HTTP/2 协议是基于 HTTPS 的,所以 HTTP/2 的安全性也是有保障的

优点

那 HTTP/2 相比 HTTP/1.1 性能上的改进:

  • 头部压缩
  • 二进制格式
  • 并发传输
  • 服务器主动推送资源
头部压缩
  • http1.1 报文中 Header 部分存在的问题:含很多固定的字段,比如 Cookie、User Agent、Accept 等;可以重复利用的字段
  • http2.0 使用 HPACK 算法,通过维护静态字典;动态字典;Huffman 编码(压缩算法);(HPACK 算法的三个组成部分)
  • 客户端和服务器两端都会建立和维护「字典」,用长度较小的索引号表示重复的字符串,再用 Huffman 编码压缩数据,可达到 50%~90% 的高压缩率。
二进制格式

http2 厉害的地方在于将 http1 的文本格式改成二进制格式传输数据,极大提高了 HTTP 传输效率,而且二进制数据使用位运算能高效解析。

并发传输

虽然http1.1 也有个类似的管道传输的机制存在,但是有些个问题!客户端可以不需要等服务器返回结果就可以发起请求,但是每一个请求都需要等上一个结果完成了之后,才能收到结果,这也造成了一定的局限性。

而http2.0 就没有了这些顾虑,通过 Stream 这个设计,多个 Stream 复用一条 TCP 连接,达到并发的效果:不同 Stream 的帧是可以乱序发送的(因此可以并发不同的 Stream ),因为每个帧的头部会携带 Stream ID 信息,所以接收端可以通过 Stream ID 有序组装成 HTTP 消息

缺点

tcp队头阻塞

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

http3.0

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

  1. 队头阻塞;
  2. TCP 与 TLS 的握手时延迟;
  3. 网络迁移需要重新连接;

HTTP/2 队头阻塞的问题是因为 TCP,所以 HTTP/3 把 HTTP 下层的 TCP 协议改成了 UDP!

image.png UDP 发送是不管顺序,也不管丢包的,所以不会出现像 HTTP/2 队头阻塞的问题。大家都知道 UDP 是不可靠传输的,但基于 UDP 的 QUIC 协议 可以实现类似 TCP 的可靠性传输。

QUIC 有以下 3 个特点。

  • 无队头阻塞
  • 更快的连接建立
  • 连接迁移

无队头阻塞

QUIC 有自己的一套机制可以保证传输的可靠性的。当某个流发生丢包时,只会阻塞这个流,其他流不会受到影响,因此不存在队头阻塞问题。

更快的连接建立

HTTP/3 的 QUIC 协议并不是与 TLS 分层,而是 QUIC 内部包含了 TLS,它在自己的帧会携带 TLS 里的“记录”,再加上 QUIC 使用的是 TLS/1.3,因此仅需 1 个 RTT 就可以「同时」完成建立连接与密钥协商,如下图: image.png

连接迁移

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

总结

  • http1.1:
    • 支持长链接,header自定义,明文传输;但是不支持服务器主动发起请求
    • 但臃肿的请求头造成的资源浪费,明文传输可能导致的信息泄露,还有请求过程中请求阻塞造成的对头堵塞也是不可忽视的。
  • http2.0
    • 针对于1.1的一些缺陷:
      • http2.0使用 HPACK 算法对头部进行了压缩
      • 而且将将 http1.1 的文本格式改成二进制格式传输数据,提高了传输效率;
      • 并且支持并发传输
      • 而且协议是基于https的安全性也有了保障
    • 但是http2.0是基于tcp协议的,还是会出现tcp队头阻塞的情况
  • http3.0
    • 采用QUIC协议
      • 不需要http2.0分批次来握手,先 TCP 握手,再 TLS 握手,可以快速建立连接
      • 无TCP队头堵塞问题(因为tcp换成了udp)
      • 移动设备的网络变化后,导致 IP 地址变化了,只要仍保有上下文信息,就可以复用原连接

HTTP 常见状态码

  • 1xx系列(信息,表示临时响应并需要请求者继续执行操作)
    • 100 继续请求
    • 101 切换协议
  • 2xx系列
    • 200 成功
    • 204 服务器成功处理了请求,但没有返回任何内容
  • 3xx系列
    • 301 永久重定向
    • 302 临时重定向
    • 303 临时重定向,只是它希望使用GET方法重定向到新的URL上
    • 304 表示资源未被修改;可以使用缓存
  • 4xx系列(客户端错误)
    • 400 请求语法错误
    • 401 要求身份验证
    • 403 服务器拒绝请求
    • 404 请求资源不存在
    • 405 禁用请求中指定的方法
  • 500系列
    • 500 服务器错误
    • 502 网关错误
    • 503 服务不可用
    • 504 网关超时

HTTP 常见 header

  1. Host 字段:客户端发送请求时,用来指定服务器的域名
  2. Content-Length 字段:服务器在返回数据时,表明本次回应的数据长度。
  3. Connection 字段:为了兼容老版本的 HTTP,需要指定 Connection 首部字段的值为 Keep-Alive
  4. Content-Type:告诉客户端,本次数据是什么格式
  5. Content-Encoding:字段说明数据的压缩方法

HTTPS

HTTP 和 HTTPS 的区别:

  • 安全性
    • HTTP 信息是明文传输,存在安全风险的问题
    • HTTPS 则是 HTTP与TCP 层之间加入了 SSL/TLS 协议,使得报文能够加密传输
    • HTTPS 协议需要向 CA(证书权威机构)申请数字证书,来保证服务器的身份是可信的
  • 连接建立
    • HTTP只需要进行 TCP 的三次握手就可以建立连接
    • HTTPS 在 TCP 三次握手之后,还需进行 SSL/TLS 的握手过程,才可建立连接
  • 端口不一样
    • HTTP 默认是 80
    • HTTPS 默认是 443

HTTPS 如何进行加密的

https进行加密的主要方法有以下几点:

  1. 使用SSL/TLS协议进行加密通信

    • HTTPS中:客户端和服务器端会先进行SSL握手、协商加密算法、交换密钥等信息。
    • 这使得双方在不泄露密钥的情况下,建立加密信道。
  2. 使用公钥加密传输对称密钥

    • 在SSL握手时,服务端会把证书中的公钥发送给客户端
    • 客户端使用这个公钥加密一个随机生成的对称加密密钥然后发送给服务器
    • 服务器使用自己的私钥解密获得这个对称密钥。这样双方就同时持有了这个密钥来进行后续的加密通信。
  3. 使用对称加密算法加密数据

获得对称密钥后,客户端和服务器会使用对称加密算法(如AES、RC4等)来加密传输的数据。对称加密速度更快,能提供更好的性能。

  1. 使用消息认证码保证数据完整性

除了加密数据,HTTPS还会采用HMAC等消息认证码算法来验证数据完整性,防止数据在传输过程中被篡改。

  1. 使用证书验证服务器身份

客户端会验证服务端证书的有效性,包括校验证书颁发机构,证书有效期等,来验证服务器的身份,避免中间人攻击。

image.png

总结一下

总结HTTPS的加密原理:

  1. 首先,浏览器向网站服务器发出连接请求。
  2. 服务器回传自己的数字证书,这就是公钥。公钥可以被所有人获取,类似于银行账户。
  3. 浏览器检查证书是否 Valid,以确认服务器的身份。
  4. 如果证书检查通过,浏览器会生成一个对话密钥,并用服务器传来的公钥加密这个密钥,再发回服务器。
  5. 服务器用自己的私钥(保密密钥,类似银行密码)解密,获取这个对话密钥。
  6. 到此服务器和浏览器都持有这个共同的对话密钥了。后续通信都用这个密钥加密。
  7. 这样客户端和服务器之间的通信就可以安全地加密传输,不怕被窃听了。
  8. 最后,浏览器还会校验接收数据的完整性,防止在传输过程中被修改。

这样通过 HTTPS 的加密机制,可以防止数据被窃取、篡改,保证通信安全。核心就是利用了公钥加密和对称加密相结合的方式。

image.png

什么是Cookie

在Web中,它被用作浏览器中存储的数据。Cookie都是name=value的结构,name和value都为字符串。

怎么设置Cookie

  1. 在首次访问网站时,浏览器发送请求中并未携带Cookie。
  2. 浏览器看到请求中未携带Cookie,在HTTP的响应头中加入Set-Cookie。
  3. 浏览器收到Set-Cookie后,会将Cookie保存下来
  4. 下次再访问该网站时,HTTP请求头就会携带Cookie。

Cookie共享问题

Cookie的作用域和域名有关,和协议以及端口无关

通过 Domain

如果我们在设置cookie时同时设置了domain=baidu.com,则该Cookie可以在baidu.com下的任何域名内生效。比如:

  • baidu.com
  • a.baidu.com
  • b.baidu.com
  • c.d.baidu.com 注意,这种方法只适用于 Cookie 和 iframe 窗口,LocalStorage 和 IndexDB 无法通过这种方法

通过设置path

希望Cookie仅仅在部分路径下生效,就可以使用Path进行限制 只在/abc路径下生效。比如:

  • baidu.com 不生效
  • baidu.com/abc 生效
  • baidu.com/abc/def 生效
  • baidu.com/qaz 不生效
  • baidu.com/qaz/abc 不生效

如何在跨域请求中携带cookie

解决方案

1.nginx反向代理

使用 nginx 反向代理实现跨域,是最简单的跨域方式。只需要修改 nginx 的配置即可解决跨域问题,支持所有浏览器,支持 session,不需要修改任何代码,并且不会影响服务器性能。

反向代理原理就是将前端的地址和后端的地址用nginx用同一个地址代理,如5500端口和3000端口都转到3003端口下

server
{
   listen 3003;
   server_name localhost;
   ##  = /表示精确匹配路径为/的url,真实访问为http://localhost:5500
   
   location = / {
       proxy_pass http://localhost:5500;
   }
   
   ##  /no 表示以/no开头的url,包括/no1,no/son,或者no/son/grandson
   ##  真实访问为http://localhost:5500/no开头的url
   ##  若 proxy_pass最后为/ 如http://localhost:3000/;匹配/no/son,则真实匹配为http://localhost:3000/son
   
   location /no {
       proxy_pass http://localhost:3000;
   }
   
   ##  /ok/表示精确匹配以ok开头的url,/ok2是匹配不到的,/ok/son则可以
   
   location /ok/ {
       proxy_pass http://localhost:3000;
   }
}

jsonp
<script type="text/javascript">
function callbackFun(data)
{
    console.log(111);
    console.log(data.name);
    //data.age = 10000000;
    //alert(0000);
}

$("#submit").on("click", function(){     
        $.ajax({
            type:"post",
            url:"http://xxxx",
            dataType:'jsonp',
            jsonp:'mycallback',//传递给后台,以获得jsonp回调函数名
            jsonpCallback:'callbackFun',
            success:function(data) {   
                console.log(data.age);
            }
        });
    })
</script>

3.前后端搭配

前端设置 withCredentials 为true,后端设置Header的方式
服务器:

//跨域请求携带Cookie
//如果需要把Cookie发到服务端,需要指定Access-Control-Allow-Credentials字段为true;
response.setHeader("Access-Control-Allow-Credentials", "true");

//Access-Control-Allow-Origin的值不能为”*”,必须是一个确定的域,与前端配合
response.setHeader("Access-Control-Allow-Origin",request.getHeader("Origin"));

//表明服务器支持的头信息字段
response.setHeader("Access-Control-Allow-Headers","content-type");

前端

$.ajax({
    type: "POST",
    url: "实际的请求地址",
    data: {参数:参数值},
    dataType: "json",
    crossDomain:true, //设置跨域为true
    xhrFields: {
        withCredentials: true //默认情况下,标准的跨域请求是不会发送cookie的
    },
    success: function(data){
        alert("请求成功");      
    }
});

浏览器同源策略

同源策略是一个很重要的安全策略,它能帮助阻隔恶意文档,减少可能被攻击的媒介。

如果两个 URL 的协议、端口(如果有指定的话)和主机都相同的话,则这两个 URL 是同源的。

举个例子

当前页面url被请求页面url是否跨域原因
www.test.com/www.test.com/index.html同源
www.test.com/www.test.com/index.html跨域协议不同(http/https)
www.test.com/www.baidu.com/跨域主域名不同(test/baidu)
www.test.com/blog.test.com/跨域子域名不同(www/blog)
www.test.com:8080/www.test.com:7001/跨域端口号不同(8080/7001)

非同源(跨域)会遇到的问题

  1. 无法读取非同源网页的 Cookie、LocalStorage 和 IndexedDB
  2. 无法接触非同源网页的 DOM
  3. 无法向非同源地址发送 AJAX 请求

跨域怎么处理

跨域问题在我们日常开发中也不少见,这是一个开发必须遇到的问题。

1. 设置 Domain、Path 共享 Cookie

这里在上面说过,就简单说一下吧(此方案仅限主域相同,子域不同的跨域应用场景。)

domain: xxxx 
path:/collector/apm

2.window.postMessage()

跨文档通信 API(window.postMessage),它可用于解决以下方面的问题:

  • 页面和其打开的新窗口的数据互相传递
  • 多窗口之间消息传递
  • 页面与嵌套的iframe消息传递
  • 上面三个场景的跨域数据传递

举个例子

页面A

const child = window.open('http://www.child.com', '子页面')
// 第一个参数代表发送的内容,第二个参数代表接收消息窗口的url
child.postMessage('hello world!', 'http://test2.com')

页面B

// 监听 message 消息
window.addEventListener('message', function (e) {
  console.log(e.source); // e.source 发送消息的窗口
  console.log(e.origin); // e.origin 消息发向的网址
  console.log(e.data);   // e.data   发送的消息
},false);

3.JSONP

  • 优点:
    • 就是简单适用
    • 兼容性好(兼容低版本IE)
  • 缺点:
    • 只支持get请求,不支持post请求

举个例子

原生实现

<script src="http://example.com/api/data?callback=dosomething"></script>
// 向服务器test.com发出请求,该请求的查询字符串有一个callback参数,用来指定回调函数的名字
 
// 处理服务器返回回调函数的数据
<script type="text/javascript">
    function dosomething(res){
        // 处理获得的数据
        console.log(res.data)
    }
</script>

  • jq中可以用$ajax
  • vue中可以用vue-resource的$http.jsonp来实现

4.CORS

CORS 是 W3C 标准,属于跨源 AJAX 请求的根本解决方法。

  • 普通跨域请求:只需服务器端设置Access-Control-Allow-Origin
  • 带cookie跨域请求:前后端都需要进行设置
    • 前端:withCredentials 判断是否带有cookie
    • 后端:
      • Access-Control-Allow-Credentials 是否允许跨域
      • Access-Control-Allow-Origin 是否允许携带cookie

5.webpack本地代理

image.png

6.nginx反向代理

使用 nginx 反向代理实现跨域,是最简单的跨域方式。只需要修改 nginx 的配置即可解决跨域问题,支持所有浏览器,支持 session,不需要修改任何代码,并且不会影响服务器性能。

浏览器缓存策略

强制缓存优于协商缓存生效

强制缓存

浏览器判断缓存是否过期,只要判断未过期,则立刻使用本地缓存,决定权取决于浏览器

  • cache-control 和 expries (前者的权重更高一点)来判断缓存时间
  • 浏览器判断缓存是否过期
    • 第一次加重获取过期失效
    • 之后再次加载前先判断是否过期
    • 如果过期 => 重新请求
    • 未过期 => 直接使用缓存

协商缓存

与服务器协商后根据协商结果来判断是否使用本地缓存

  • 协商失败 => 200
  • 协商成功 => 304