前端网络知识初步

前端网络知识初步

1.七层网络模型简要

1.1 七层网络模型总结

七层网络模型是理论上的模型,现实中是TCP/IP网络模型,相信学过计算机网络这门课程的对此并不陌生。大致图如下:
在这里插入图片描述
从下往上依次为:物理层、数据链路层、网络层、传输层、会话层、表示层和应用层。

1.2 七层网络模型详解

  • 物理层(比特流)
  • 数据链路层(数据帧)
    在之前物理层的数据基础上会加上head头部,里面标明数据发送者、数据接收者,如mac地址
  • 网络层(数据包)
    主要任务是选择合适的路由、交换报文;
  • 传输层(数据段)
    定义端口号、控流和校验。两个常见协议tcp和udp.
    tcp:面向连接并且可靠.因为有三次握手和四次挥手,但这样子速度会降低;
    udp:没有三次握手和四次挥手,所以速度快,常用于直播、游戏。具有较强的时效性;
  • 会话层(报文)
    负责建立、管理、终止表示层实体之间的通信会话;这一层提供了建立检查点和恢复方案的方法
  • 表示层(报文)
    是网络中的翻译官,使应用中的程序可以解释数据的含义。包含的服务有加密(发送者的表示层进行加密,接收者的表示层进行解密),编码和解码(对图片等格式进行编码解码,png、ascii图片是人能看懂的,需要把它转换成计算机能看懂的编码)。主要是统一不同计算机间数据存储和表示的内部差异;
  • 应用层(报文)
    在应用层交互的数据单元被称为报文。该层协议定义了应用程序之间的交互规则。这一层经常被用到,如ajax发送http请求;域名系统DNS;邮件协议SMTP,websocket长连接、SSH协议。

2 TCP/IP协议简要

2.1 TCP/IP协议是什么

TCP/IP,是传输控制协议/网际协议。它不仅仅指的是TCP和IP两个协议,而是指由ftp、smtp、tcp、udp、ip等众多协议构成的协议簇。

只是因为TCP/IP协议中TCP和IP协议最有代表性,所以统称为TCP/IP协议族。

2.2 划分

TCP/IP协议族按层次分为五层体系和四层体系。这一部分简单了解即可。
在这里插入图片描述
五层体系的协议结构是综合OSI和TCP/IP优点的一种协议,包含应用层、传输层、网络层、数据链路层和物理层。

五层协议的体系结构只是为了介绍网络原理而设计的。实际应用还是TCP/IP四层体系结构,包括应用层、传输层、网络层、网络接口层。

3.TCP VS UDP

TCP(传输控制协议)和UDP(用户数据报协议)都是传输层协议,但它们之间存在明显的区别。

3.1 UDP

面向报文的,无连接的,不可靠的,没有三次握手四次挥手协议,所以实时性比较强。应用层交下来的报文,不合并并且不拆分,途中出现丢包现象也不负责重发,没有流量控制避免网络拥塞现象。

3.2 TCP

面向比特流的,面向连接的,可靠的,有三次握手协议和四次挥手协议,安全性强,速度没有UDP快。应用层交下来的报文,会进行拆分和合并。

3.3 区别

下面是TCP和UDP区别的总结图:
在这里插入图片描述

  • TCP是面向连接的协议,有三次握手四次挥手协议;UDP是无连接的协议,没有三次握手四次挥手;
  • TCP提供可靠的服务,有流量控制;UDP没有流量控制;
  • TCP面向字节流,会拆分报文变成一个一个的报文段;UDP是面向报文,不会拆分报文;

3.4 应用场景

两者应用场景如下图:
在这里插入图片描述

  • TCP:通常用于需要可靠传输的场景,如文件传输、电子邮件等
  • UDP:则适用于对实时性要求较高、对丢包容忍度较高的场景,如视频直播、在线游戏等。
    如DNS,TFTP(简单文件传输协议,基于UDP的客户/服务器模式,较为简单但安全性较差)都是基于UDP。

4.TCP三次握手四次挥手请求

4.1 三次握手(建立连接时候)

在这里插入图片描述

描述如下:

  • 客户端发送SYN包到服务器:客户端首先向服务器发送一个SYN包,这个包中包含客户端的初始序列号。这个步骤的目的是客户端向服务器请求建立连接。

  • 服务器发送SYN+ACK包到客户端:服务器在收到客户端的SYN包后,会发送一个SYN+ACK包作为响应。这个包中包含了服务器的初始序列号,并对客户端的SYN进行确认。

  • 客户端发送ACK包到服务器:客户端收到服务器的SYN+ACK包后,会发送一个ACK包作为最后的确认。这个ACK包中包含了对服务器SYN的确认。

4.2 四次挥手(断开连接时候)

在这里插入图片描述
描述如下:

  • 客户端发送FIN包到服务器:当客户端想要关闭连接时,会首先发送一个FIN包给服务器,表示客户端已经完成了数据的发送,并希望关闭连接。
  • 服务器发送ACK包到客户端:服务器收到客户端的FIN包后,会发送一个ACK包作为确认。此时,连接处于半关闭状态,即客户端不再发送数据,但服务器仍可以发送数据。
  • 服务器发送FIN包到客户端:当服务器也完成了数据的发送,并希望关闭连接时,会发送一个FIN包给客户端。
  • 客户端发送ACK包到服务器:客户端收到服务器的FIN包后,会发送一个ACK包作为最后的确认。至此,TCP连接被完全关闭。

需要注意的是,在四次挥手的过程中,客户端和服务器都有可能先发起关闭请求,而且为了确保数据能够可靠地传输,某些步骤中还会涉及到等待时间(如TIME-WAIT状态)的设置。

下面来用通俗的话解释一下上面这个过程:
简单来说,就是客户端想要断开连接时,会发送一个FIN报文。服务器收到这个报文后,并不急着关闭连接,因为它可能还有数据没发完。所以,服务器会先发一个ACK报文,告诉客户端:“我收到了你的请求,但我还需要处理一些数据,稍等一下。”

这个时候,客户端就会进入FIN_WAIT状态,继续等待服务器的FIN报文。等服务器处理完所有数据,就会发送一个FIN报文给客户端,告诉它:“好了,我这边数据发完了,可以关闭连接了。”

客户端收到这个FIN报文后,还是会有点不放心,怕服务器不知道要关闭。所以它会再发一个ACK报文,然后进入TIME_WAIT状态。这个TIME_WAIT状态就是等待2MSL时间。如果服务器在这个时间内没收到ACK,它可以重传。

4.3 一些问题

4.3.1 为什么不是两次握手呢?

如果是两次握手,客户端可以确定自己发送的数据服务端能接收到,服务端发送的数据客户端可以接收到;但服务端只能确定客户端发送的数据自己能接收到,但不能确定自己发送的数据客户端可以接收到。

4.3.2 四次挥手的FIN_WAIT2阶段是干嘛的?

在上面FIN_WAIT2阶段,如果有未完成的响应,在这个阶段也是会处理完成的。直到把所有任务都完成,服务端才会发送第三次挥手。

4.3.3 四次挥手释放连接时,等待2MSL的意义

MSL是Maximum Segment Lifetime的英文缩写,可译为“最长报文段寿命”,它是任何报文在网络上存在的最长时间,超过这个时间报文将被丢弃。

第四次挥手之所以要等待2MSL时间,是为了保证双方都能正常关闭连接,并避免对后续的连接造成干扰。
在这里插入图片描述
为了保证客户端发送的最后一个ACK报文段能够到达服务器。因为这个ACK有可能丢失,从而导致处在LAST-ACK状态的服务器收不到对FIN-ACK的确认报文。服务器会超时重传这个FIN-ACK,接着客户端再重传一次确认,重新启动时间等待计时器。最后客户端和服务器都能正常的关闭。假设客户端不等待2MSL,而是在发送完ACK之后直接释放关闭,一但这个ACK丢失的话,服务器就无法正常的进入关闭。

4.4 完整的三次握手四次挥手

在这里插入图片描述

5.CDN内容分发网络

5.1 概述

请求一个网站,不是直接从源服务器拿到数据,而是找到离发送方最近的CDN节点拿到信息,CDN节点是一个缓存源站信息的代理服务器。

5.2 原理

CDN通过在现有的互联网中增加一层新的缓存服务器,将网站的内容发布到最接近用户的网络节点上。当用户请求内容时,CDN会根据地理位置将请求路由到最近的缓存服务器,从而减少数据传输时间,加快加载速度。如果缓存服务器上没有所需内容,则会从源服务器获取内容并缓存,以便下次快速提供。

5.3 作用

  • 加速内容加载‌:通过在全球分布的服务器上缓存内容,大幅减少数据传输时间,加速网页和多媒体内容的加载速度。
  • 增强网站稳定性‌:分散用户请求到多个服务器,减轻源服务器的负荷,提高网站的可用性和抗压能力。
  • 安全防护‌:通常包括DDoS攻击防御、DNS劫持和内容劫持保护等安全措施,确保内容的安全分发。
  • 全球覆盖‌:CDN的全球服务器网络意味着企业可以无缝地向世界各地的用户分发内容,解决跨地域访问速度慢的问题。
  • 成本效益‌:通过减少对源站带宽的依赖和优化内容的传输方式,CDN有助于降低网络带宽成本,同时减少了企业在全球范围内构建和维护自己服务器网络的必要性。

5.4 配置CDN

这个过程比较复杂,但都有一定的流程。上网查看教程即可。

6.ajax、fetch、axios区别

6.1 ajax

6.1.1 ajax是一种技术统称

它的全称是:Asynchronous JavaScript And XML,翻译过来就是“异步的 Javascript 和 XML”。

很多小伙伴可能会误以为 Ajax 是发请求的一种方式,或者把XMLHttpRequest与Ajax划等号,其实这是错误和片面的。

正解:

Ajax 是一个技术统称,是一个概念模型,它囊括了很多技术,并不特指某一技术,它很重要的特性之一就是让页面实现局部刷新。

简单来说,Ajax 是一种思想,XMLHttpRequest 只是实现 Ajax 的一种方式。其中 XMLHttpRequest 模块就是实现 Ajax 的一种很好的方式,这也是很多面试官喜欢让面试者手撕的代码之一。

6.1.2 关于XMLHttpRequest

(1)new XMLHttpRequest()
使用 XMLHttpRequest可以通过JavaScript 发起HTTP请求,接收来自服务器的响应,并动态地更新网页中的内容。这种异步通信方式不会阻塞用户界面,有利于增强用户体验。

(2)open方法
我们需要使用open() 方法打开一个请求,该方法会初始化一个请求,但并不会发送请求。它有几个参数

  • method:请求的 HTTP 方法,例如 GET、POST 等。
  • url:请求的 URL 地址。
  • async:是否异步处理请求,默认为 true,即异步请求。

(3)onreadystatechange
它是一个回调函数,在每次状态发生变化时被调用。

  • readyState 0:未初始化,XMLHttpRequest 对象已经创建,但未调用 open 方法。
  • readyState 1:已打开,open 方法已经被调用,但 send 方法未被调用。
  • readyState 2:已发送,send 方法已经被调用,请求已经被服务器接收。
  • readyState 3:正在接收,服务器正在处理请求并返回数据。
  • readyState 4:完成,服务器已经完成了数据传输。

(4)send
向后端传递参数 例如 xhe.send(params)

6.1.3 XMLHttpRequest使用案例

发送一个get 请求:

const xhr = new XMLHttpRequest();
xhr.open('GET', 'http://localhost:3000/api/txt')
xhr.onload = function() {
  if (xhr.status === 200) {
        document.querySelector('#result').innerText = xhr.responseText;
    }
    else {
       console.log('Request failed.  Returned status of ' + xhr.status);
   }
};
xhr.send(null);

发送post 请求 json:

const xhr = new XMLHttpRequest();
xhr.open('POST', 'http://localhost:3000/api/post')
xhr.setRequestHeader('Content-Type', 'application/json');
xhr.onload = function() {
  if (xhr.status === 200) {
        document.querySelector('#result').innerText = xhr.responseText;
    }
    else {
       console.log('Request failed.  Returned status of ' + xhr.status);
   }
};
xhr.send(JSON.stringify({name: 'zhangsan', age: 18}));

发送post 请求 application/x-www-form-urlencoded:

const xhr = new XMLHttpRequest();
xhr.open('POST', 'http://localhost:3000/api/post')
xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');
xhr.onload = function() {
  if (xhr.status === 200) {
        document.querySelector('#result').innerText = xhr.responseText;
    }
    else {
       console.log('Request failed.  Returned status of ' + xhr.status);
   }
};
//传递json数据一定要序列化
xhr.send('name=zhangsan&age=18');

上传图片 multipart/form-data:
浏览器会自动设置请求头为 multipart/form-data:
在这里插入图片描述
并且浏览器会自动添加 boundary 分割线:
在这里插入图片描述

document.querySelector('#file').addEventListener('change', function () {
    const xhr = new XMLHttpRequest();
    xhr.open('POST', 'http://localhost:3000/api/upload')
    //这里不需要设置请求头,因为浏览器会自动设置请求头为 multipart/form-data:
    //并且浏览器会自动添加 boundary 分割线:
    xhr.onload = function () {
        if (xhr.status === 200) {
            document.querySelector('#result').innerText = xhr.responseText;
        }
        else {
            console.log('Request failed.  Returned status of ' + xhr.status);
        }
    };

    let file = this.files[0];
    let formData = new FormData();
    //这里的file是key,与后端设置的要一致
    formData.append('file', file);
    xhr.send(formData);
});

中断请求 和 设置超时时间:
中断请求只需要调用xhr.abort()即可
并且会有一个中断的回调:

xhr.addEventListener('abort', function (event) {
    console.log('我被中断了');
});

超时时间可以设置timeout 参数,同样有一个超时回调:

xhr.addEventListener('timeout', function (event) {
     console.log('超时啦');
});

获取进度:
可以监听 progress,在监听器中,我们通过 event.loaded 和 event.total 属性获取已上传数据量和总数据量,并计算上传进度,最后将进度显示在页面上。

xhr.addEventListener('progress', function (event) {
document.querySelector('#progress').innerText = `${(event.loaded / event.total *      100).toFixed(2)}%`;
});

6.2 fetch

6.2.1 fetch概述

Fetch 是在 ES6 出现的,它使用了 ES6 提出的 promise 对象。它是 XMLHttpRequest 的替代品。

很多小伙伴会把它与 Ajax 作比较,其实这是不对的,我们通常所说的 Ajax 是指使用 XMLHttpRequest 实现的 Ajax,所以真正应该和 XMLHttpRequest 作比较。

Fetch 是一个 API,它是真实存在的,它是基于 promise 的。

所以这里就和 Ajax 又很大不同了,一个是思想,一个是真实存在的 API,不过它们都是用来给网络请求服务的。

6.2.2 fetch VS xhr

fetch 和 XMLHttpRequest(XHR)都是前端与服务器进行数据交互的常用方式,它们各有优缺点,下面是它们的比较:

  • API 设计和使用方式
    fetch 的 API 设计更加现代化、简洁和易于使用,使用起来更加直观和方便。相比之下,XHR 的 API 设计比较繁琐,需要进行多个参数的配置和回调函数的处理。
  • 支持的请求方法
    fetch API 默认只支持 GET 和 POST 请求方法,而 XHR 则支持所有标准的 HTTP 请求方法。
  • 请求头部
    在 fetch 中设置请求头部的方式更加清晰和直接,可以通过 Headers 对象进行设置,而 XHR 的方式相对较为繁琐。
  • 请求体
    在发送 POST 请求时,fetch API 要求将请求体数据作为参数传递给fetch方法中的 options 对象,而 XHR 可以直接在 send() 方法中设置请求体数据。
  • 支持的数据类型
    在解析响应数据时,fetch API 提供了多种方法,包括 .json(), .blob(), .arrayBuffer() 等,而 XHR 只支持文本和二进制数据两种数据类型。
  • 跨域请求
    在进行跨域请求时,fetch API 提供了一种简单而强大的解决方案——使用 CORS(跨域资源共享)头部实现跨域请求;而 XHR 则使用了一个叫做 XMLHttpRequest Level 2 的规范,在代码编写上相对较为繁琐。

总的来说,fetch API 与 XHR 各有优缺点,具体选择哪种方式还需要根据具体情况进行考虑。平时开发中使用较多的是 fetch ,因为它使用方便、API 简洁、语法清晰,同时也支持了大多数常用的功能,可以有效地简化前端开发流程。

6.1.3 fetch使用案例

fetch 返回格式:

  • text(): 将响应体解析为纯文本字符串并返回。
  • json(): 将响应体解析为JSON格式并返回一个JavaScript对象。
  • blob(): 将响应体解析为二进制数据并返回一个Blob对象。
  • arrayBuffer(): 将响应体解析为二进制数据并返回一个ArrayBuffer对象。
  • formData(): 将响应体解析为FormData对象。

get请求:

fetch('http://localhost:3000/api/txt').then(res => {
    console.log(res);
    return res.text()
}).then(res => {
    console.log(res);
})

post请求:

fetch('http://localhost:3000/api/post',{
    method:'POST',
    headers:{
        'Content-Type':'application/json'
    },
    body:JSON.stringify({
        name:'zhangsan',
        age:18
    })
}).then(res => {
    console.log(res);
    return res.json()
}).then(res => {
    console.log(res);
})

中断请求:
使用 AbortController 的 abort方法中断

const abort = new AbortController()
fetch('http://localhost:3000/api/post',{
    method:'POST',
    headers:{
        'Content-Type':'application/json'
    },
    signal:abort.signal,
    body:JSON.stringify({
        name:'zhangsan',
        age:18
    })
}).then(res => {
    console.log(res);
    return res.json()
}).then(res => {
    console.log(res);
})

document.querySelector('#stop').addEventListener('click', () => {
        console.log('stop');
        abort.abort()
})

超时请求:
通过自己封装定时器实现

const  timeoutFn=(time=3000)=>{
  setTimeout(()=>{
    abort.abort()
  },time)
}

获取进度:
使用data.clone()方法复制了响应对象data,然后使用getReader()方法获取数据流中的reader对象,接着通过读取数据流并计算已加载字节数,实现了一个基于原生JavaScript的进度条功能。

const btn = document.querySelector('#send')
const sendFetch = async () => {
    const data = await fetch('http://localhost:3000/api/txt',{
        signal:abort.signal
    })
    //每次返回一段数据。总数据有很多段
    //fetch 实现进度条
    //拷贝一份数据,为了做返回数据用的
    const response = data.clone()
    //返回一个流
    const reader = data.body.getReader()
    //获取总长度
    const contentLength = data.headers.get('Content-Length')
    let loaded = 0
    while (true) {
        const { done, value } = await reader.read()
        //done如果为true说明结束了
        if (done) {
            break
        }
        loaded += value?.length || 0;
        const progress = document.querySelector('#progress')
        progress.innerHTML = (loaded / contentLength * 100).toFixed(2) + '%'
    }
    const text = await response.text()
    console.log(text);
}
btn.addEventListener('click', sendFetch)

携带cookie:
设置credentials:'include'

const data = await fetch('http://localhost:3000/api/txt',{
    signal:abort.signal,
    //cookie
    credentials:'include',
})

6.3 axios

6.3.1 概念

axios 是随着 Vue 的兴起而被广泛使用的,目前来说,绝大多数的 Vue 项目中的网络请求都是利用 axios 发起的。当然它并不是一个思想,或者一个原生 API,它是一个封装库

axios 是一个基于 promise 封装的网络请求库,它是基于 XHR 进行二次封装。

所以说,axios 可以说是 XHR 的一个子集,而 XHR 又是 Ajax 的一个子集。既然说它是一个库,那么我们在使用的时候就需要引入它。

6.3.2 示例代码

// 发送 POST 请求
axios({
    method: 'post',
    url: '/user/12345',
    data: {
        firstName: 'Fred',
        lastName: 'Flintstone'
    }
})

6.4 总结

Ajax、Fetch、axios三者之间的关系可以用一张图来清晰的表示,如图:
在这里插入图片描述
三者做个对比:

方式描述
Ajax一种技术统称,主要利用XHR实现网络请求
Fetch具体API,基于promise,实现网络请求
Axios一个封装库,基于XHR封装,较为推荐使用

7.SSE VS WebSocket

7.1 SSE

7.1.1 概述

SSE(Server-Sent Events)是一种用于实现服务器主动向客户端推送数据的技术,也被称为“事件流”(Event Stream)。它基于HTTP协议,是单向通信,利用了其长连接特性,在客户端与服务器之间建立一条持久化连接,并通过这条连接实现服务器向客户端的实时数据推送。

7.1.2 使用场景

  • chatGPT返回的数据,就是使用的SSE 技术
  • 实时数据大屏,如果只是需要展示,实时的数据可以使用SSE技术,而不是非要使用WebSocket

7.1.3 api用法

EventSource 对象是 HTML5 新增的一个客户端 API,用于通过服务器推送实时更新的数据和通知。在使用 EventSource 对象时,可以通过以下方法进行配置和操作:

EventSource() 构造函数:
EventSource 的构造函数接收一个 URL 参数,通过该 URL 可以建立起与服务器的连接,并开始接收服务器发送的数据。

const eventSource = new EventSource(url, options);
  • url:String 类型,表示与服务器建立连接的 URL。必填。
  • options:Object 类型,表示可选参数。常用的可选参数包括:
    • withCredentials:Boolean 类型,表示是否允许发送 Cookie 和 HTTP 认证信息。默认为 false。
    • headers:Object 类型,表示要发送的请求头信息。
    • retryInterval:Number 类型,表示与服务器失去连接后,重新连接的时间间隔。默认为 1000 毫秒。

EventSource.readyState属性:
readyState 属性表示当前 EventSource 对象的状态,它是一个只读属性,它的值有以下几个:

  • CONNECTING:表示正在和服务器建立连接。
  • OPEN:表示已经建立连接,正在接收服务器发送的数据。
  • CLOSED:表示连接已经被关闭,无法再接收服务器发送的数据。

示例如下:

if (eventSource.readyState === EventSource.CONNECTING) {
  console.log('正在连接服务器...');
} else if (eventSource.readyState === EventSource.OPEN) {
  console.log('已经连接上服务器!');
} else if (eventSource.readyState === EventSource.CLOSED) {
  console.log('连接已经关闭。');
}

EventSource.close() 方法
close() 方法用于关闭 EventSource 对象与服务器的连接,停止接收服务器发送的数据。

eventSource.close();

EventSource.onopen事件
onopen 事件表示 EventSource 对象已经和服务器建立了连接,并开始接收来自服务器的数据。当 EventSource 对象建立连接时,触发该事件。

eventSource.onopen = function(event) {
  console.log('连接成功!', event);
};

EventSource.onerror事件
onerror 事件表示在建立连接或接收服务器数据时发生了错误。当出现错误时,触发该事件。

eventSource.onerror = function(event) {
  console.log('发生错误:', event);
};

EventSource.onmessage事件
onmessage 事件表示已经接收到服务器发送的数据,当接收到数据时,触发该事件。

eventSource.onmessage = function(event) {
  console.log('接收到数据:', event);
};

需要注意的是,在使用 EventSource 对象的过程中,如果服务器没有正确地设置响应头信息(如:Content-Type: text/event-stream),可能会导致 EventSource 对象无法接收到服务器发送的数据。

7.1.4 使用示例

后端用nodejs编写:

import express from 'express';
const app = express();
app.get('/api/sse', (req, res) => {
    res.writeHead(200, {
        //把普通请求设置为sse请求
        'Content-Type': 'text/event-stream', //核心返回数据流
        'Connection': 'close'
    })
    const data = fs.readFileSync('./index.txt', 'utf8')
    const total = data.length;
    let current = 0;
    //mock sse 数据
    let time = setInterval(() => {
        console.log(current, total)
        if (current >= total) {
            console.log('end')
            clearInterval(time)
            return
        }
        //返回自定义事件名,通过event设置事件名
        //默认为message
        res.write(`event:lol\n`)
        /返回数据
        res.write(`data:${data.split('')[current]}\n\n`)
        current++
    }, 300)
})
app.listen(3000, () => {
    console.log('Listening on port 3000');
});

index.txt中内容:

悲索之人烈焰加身,堕落者 不可饶恕,我即是引路的灯塔,也是净化的清泉。
永恒燃烧的羽翼,带我脱离凡间的沉沦,圣火将你洗涤。
今由烈火审判,于光明中得救。
利刃在手,制裁八方!

前端调用:

document.addEventListener('keydown',(e)=>{
  //按回车键
  if(e.keycode===13){
    const sse = new EventSource('http://localhost:3000/api/sse' )
    sse.addEventListener('open', (e) => {
       console.log(e.target)
    })
    //对应后端nodejs自定义的事件名lol
    sse.addEventListener('lol', (e) => {
       document.getElementById('message-box').innerHTML+=e.data;
    })
  }
})

大致效果如下:
在这里插入图片描述

7.2 WebSocket

7.2.1 概述

WebSocket 是一种在单个TCP连接上进行全双工通信的协议,它可以让客户端和服务器之间进行实时的双向通信。WebSocket 使用一个长连接,在客户端和服务器之间保持持久的连接,从而可以实时地发送和接收数据。在 WebSocket 中,客户端和服务器之间可以互相发送消息,客户端可以使用JavaScript 中的 WebSocket API发送消息到服务器,也可以接收服务器发送的消息。

7.2.2 常见应用场景

  • 实时聊天
    WebSocket能够提供双向、实时的通信机制,使得实时聊天应用能够快速、高效地发送和接收消息,实现即时通信。
  • 实时协作
    用于实时协作工具,如协同编辑文档、白板绘画、团队任务管理等,团队成员可以实时地在同一页面上进行互动和实时更新。
  • 实时数据推送
    用于实时数据推送场景,如股票行情、新闻快讯、实时天气信息等,服务器可以实时将数据推送给客户端,确保数据的及时性和准确性。
  • 多人在线游戏
    实时的双向通信机制,适用于多人在线游戏应用,使得游戏服务器能够实时地将游戏状态和玩家行为传输给客户端,实现游戏的实时互动。
  • 在线客服
    WebSocket可以用于在线客服和客户支持系统,实现实时的客户沟通和问题解决,提供更好的用户体验,减少等待时间。

7.2.3 使用示例

let socket = new WebSocket("ws://example.com/socketserver");

socket.onopen = function(event) {
  //发送信息给服务端
  socket.send("Hello server!");
};

socket.onmessage = function(event) {
  console.log("Received message from server: " + event.data);
};

socket.onerror = function(event) {
  console.log("WebSocket error: " + event.error);
};

socket.onclose = function(event) {
  console.log("WebSocket connection closed with code " + event.code);
};

websocket在vue中的使用:

<template>
  <div>
    <input v-model="message" @keyup.enter="sendMessage">
    <button @click="sendMessage">Send</button>
    <ul>
      <li v-for="msg in messages">{{ msg }}</li>
    </ul>
  </div>
</template>

<script>
export default {
  data() {
    return {
      message: '',
      messages: [],
      socket: null
    }
  },
  created() {
    this.socket = new WebSocket('ws://localhost:3000')
    this.socket.addEventListener('message', this.handleMessage)
  },
  beforeDestroy() {
    this.socket.close()
  },
  methods: {
    sendMessage() {
      this.socket.send(this.message)
      this.message = ''
    },
    handleMessage(event) {
      this.messages.push(event.data)
    }
  }
}
</script>

7.3 SSE和WebSocket的区别

SSE和WebSocket都是实现服务器向客户端实时推送数据的技术,但它们在某些方面还是有一定的区别。

  • 技术实现
    SSE基于HTTP协议,利用了其长连接特性,通过浏览器向服务器发送一个HTTP请求,建立一条持久化的连接;而 WebSocket 则是通过特殊的升级协议( HTTP/1.1 Upgrade或者 HTTP/2 )建立新的 TCP 连接,与传统 HTTP 连接不同。
  • 数据格式
    SSE可以传输文本和二进制格式的数据,但只支持单向数据流,即只能由服务器向客户端推送数据;WebSocket支持双向数据流,客户端和服务器可以互相发送消息,并且没有消息大小限制
  • 连接状态
    SSE 的连接状态仅有三种:已连接、连接中、已断开。连接状态是由浏览器自动维护的,客户端无法手动关闭或重新打开连接。而 WebSocket 连接的状态更灵活,可以手动打开、关闭、重连等。
  • 兼容性
    SSE是标准的Web API,可以在大部分现代浏览器和移动设备上使用。但如果需要兼容老版本的浏览器(如 IE6/7/8),则需要使用 polyfill 库进行兼容。而 WebSocket 在一些老版本 Android 手机上可能存在兼容性问题,需要使用一些特殊的 API 进行处理。
  • 安全性
    SSE的实现比较简单,都是基于HTTP协议的,与普通的Web应用没有太大差异,因此风险相对较低。WebSocket则需要通过额外的安全措施(如 SSL/TLS 加密)来确保数据传输的安全性,避免被窃听和篡改,否则可能会带来安全隐患。

8.Beacon API

8.1 概述

Beacon API 是一种专门用于从网页向服务器发送小量数据的 API,主要用于统计和诊断信息的传递。它在页面卸载(如页面关闭或导航到新页面)时特别有用,因为它能够确保数据在页面卸载前成功发送到服务器。

8.2 优点

  • 可靠性高:
    在页面卸载时,能够保证数据发送成功。不受页面卸载过程的影响,确保数据可靠发送。(突然切换页面,依然上报)
  • 简洁易用:API 简单易用,只需调用一个方法即可发送数据。
  • 非阻塞:不会阻塞页面的卸载操作,提升用户体验。
  • 适用于传输小量数据:特别适合发送统计和诊断信息等小量数据。

8.3 缺点

  • fetch 和 ajax 都可以发送任意请求 而 sendBeacon 只能发送POST
  • fetch 和 ajax 可以传输任意字节数据 而 sendBeacon 只能传送少量数据(64KB 以内)
  • fetch 和 ajax 可以定义任意请求头 而 sendBeacon 无法自定义请求头
  • sendBeacon 只能传输 ArrayBuffer、ArrayBufferView、Blob、DOMString、FormData或 URLSearchParams 类型的数据
  • 如果处于危险的网络环境,或者开启了广告屏蔽插件 此请求将无效

8.4 应用场景

  • 用户行为分析:发送用户点击、页面浏览等行为数据。
  • 性能监控:发送页面加载时间、资源加载时间等性能数据。
  • 错误报告:发送 JavaScript 错误、网络错误等错误信息。
  • 应用诊断:发送应用状态、日志信息等诊断数据。

经常用于数据埋点应用场景:
在这里插入图片描述

8.5 使用示例

发送用户行为数据:

window.addEventListener('beforeunload', function() {
    const url = 'https://www.example.com/log';
    const data = JSON.stringify({ event: 'pageUnload', timestamp: Date.now() });
    navigator.sendBeacon(url, data);
});

发送性能监控数据:

window.addEventListener('beforeunload', function() {
    const url = 'https://www.example.com/performance';
    const data = JSON.stringify({
        loadTime: window.performance.timing.loadEventEnd - window.performance.timing.navigationStart,
        resources: window.performance.getEntriesByType('resource')
    });
    navigator.sendBeacon(url, data);
});

发送错误报告:

window.addEventListener('error', function(event) {
    const url = 'https://www.example.com/error';
    const data = JSON.stringify({
        message: event.message,
        source: event.filename,
        line: event.lineno,
        col: event.colno,
        error: event.error
    });
    navigator.sendBeacon(url, data);
});

发送应用诊断数据

window.addEventListener('beforeunload', function() {
    const url = 'https://www.example.com/diagnostics';
    const data = JSON.stringify({
        state: app.getState(),
        logs: app.getLogs()
    });
    navigator.sendBeacon(url, data);
});

8.6 使用最佳实践

  • 最小化数据量:尽量减少发送的数据量,以确保数据在页面卸载前能够发送成功。
  • 避免敏感信息:由于 Beacon API 无法自定义请求头,发送的数据不应包含敏感信息。
  • 多次发送:对于重要数据,可以在页面生命周期中多次发送,以提高数据传输的可靠性。
  • 合理使用 Blob:对于二进制数据,可以使用 Blob 对象进行传输。

8.7 其他注意点

ping请求是html5 新增的,并且是sendBeacon特有的。ping 请求只能携带少量数据,并且不需要等待服务端响应,因此非常适合做埋点统计,以及日志统计相关功能。
在这里插入图片描述
后端(nodejs实现):

import express from 'express';
const app = express();
app.post('/api/beacon', (req: any, res) => {
    console.log(req.body);
    res.send('ok');
});

app.listen(3000, () => {
    console.log('Listening on port 3000');
});

前端:

<button class="send">发送</button>
const send = document.querySelector('.send')
send.addEventListener('click', () => {
    let data = JSON.stringify({name:'test'})
    const blob = new Blob([data], { type: 'application/json' })
    navigator.sendBeacon('http://localhost:3000/api/beacon',blob,{
        type:"beacon"
    })
})

9.WebRTC

9.1 前端发送网络请求的方法总结

  • XMLHttpRequest
  • Fetch API
  • Server-Sent Events(SSE)
  • WebSocket API
  • Beacon API
  • WebRTC

关于这些方式详细情况,本文都有讲述。也可参见这儿

9.2 概述

WebRTC(Web Real-Time Communication)是一项由W3C(World Wide Web Consortium)和IETF(Internet Engineering Task Force)共同制定的技术标准,用于实现浏览器和移动应用之间的实时音视频通信以及数据共享。WebRTC 提供了简单、强大且可扩展的 API,使开发者能够在其应用中轻松集成实时通信功能。

9.3 优点

  • 实时通信:WebRTC 提供低延迟、高质量的音视频通信,适合实时应用,如视频会议、在线教育等。
  • 点对点连接:WebRTC 通过直接的点对点连接,减少了服务器负载和通信延迟。
  • 跨平台支持:WebRTC 支持在多个平台上运行,包括桌面浏览器、移动设备和嵌入式系统。
  • 开源免费:WebRTC 是一个开源项目,开发者可以免费使用并进行修改。
  • 安全性:WebRTC 使用了 SRTP(Secure Real-time Transport Protocol)和 DTLS(Datagram Transport Layer Security)来加密音视频流和数据通道,确保通信的安全性。

9.4 缺点

  • 复杂性增加:WebRTC 的 API 复杂且涉及多种协议,开发和调试需要一定的学习成本。
  • 网络条件依赖:WebRTC 的性能和稳定性依赖于网络质量,不良的网络条件可能导致通信质量下降。
  • 兼容性问题:虽然现代浏览器都支持 WebRTC,但某些旧版浏览器或特定环境可能不完全支持。
  • NAT 和防火墙穿透:在一些网络环境下,实现 NAT(Network Address Translation)和防火墙穿透可能会遇到困难。

9.5 用法

WebRTC 的基本组成部分包括三个核心 API:MediaStream、RTCPeerConnection 和 RTCDataChannel。

9.5.1 MediaStream API

MediaStream API 用于获取和操作媒体流(音频和视频)。可以使用 getUserMedia 方法来访问用户的摄像头和麦克风。

navigator.mediaDevices.getUserMedia({ video: true, audio: true })
  .then(stream => {
    const videoElement = document.querySelector('video');
    videoElement.srcObject = stream;
  })
  .catch(error => {
    console.error('Error accessing media devices.', error);
  });

9.5.2 RTCPeerConnection API

RTCPeerConnection 是 WebRTC 的核心组件,用于建立和控制点对点连接。

创建 RTCPeerConnection

const configuration = {
  iceServers: [
    { urls: 'stun:stun.l.google.com:19302' }
  ]
};
const peerConnection = new RTCPeerConnection(configuration);

添加媒体流到连接

navigator.mediaDevices.getUserMedia({ video: true, audio: true })
  .then(stream => {
    stream.getTracks().forEach(track => peerConnection.addTrack(track, stream));
  })
  .catch(error => {
    console.error('Error accessing media devices.', error);
  });

处理 ICE 候选
ICE(Interactive Connectivity Establishment)候选是 WebRTC 用于寻找最佳路径以建立点对点连接的机制。

peerConnection.onicecandidate = event => {
  if (event.candidate) {
    // 发送 ICE 候选到远端对等体
    sendCandidateToRemote(event.candidate);
  }
};

创建和交换 SDP
SDP(Session Description Protocol)是用于描述多媒体通信会话的格式。对等体之间需要交换 SDP 信息来建立连接。
创建 Offer

peerConnection.setRemoteDescription(new RTCSessionDescription(offer))
  .then(() => {
    return peerConnection.createAnswer();
  })
  .then(answer => {
    return peerConnection.setLocalDescription(answer);
  })
  .then(() => {
    // 发送 SDP Answer 到远端对等体
    sendAnswerToRemote(peerConnection.localDescription);
  })
  .catch(error => {
    console.error('Error handling offer.', error);
  });

处理 Answer

peerConnection.setRemoteDescription(new RTCSessionDescription(answer))
  .catch(error => {
    console.error('Error setting remote description.', error);
  });

9.5.3 RTCDataChannel API

RTCDataChannel 提供了在对等体之间发送任意数据的功能。

创建 Data Channel:

const dataChannel = peerConnection.createDataChannel('chat');
 
dataChannel.onopen = () => {
  console.log('Data channel is open');
};
 
dataChannel.onmessage = event => {
  console.log('Received message:', event.data);
};

接收 Data Channel

peerConnection.ondatachannel = event => {
  const dataChannel = event.channel;
 
  dataChannel.onopen = () => {
    console.log('Data channel is open');
  };
 
  dataChannel.onmessage = event => {
    console.log('Received message:', event.data);
  };
};

9.6 使用示例

以下是一个完整的示例代码,展示了如何使用 WebRTC API 来建立一个简单的音视频通信和数据通道。
HTML:

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>WebRTC Example</title>
</head>
<body>
  <video id="localVideo" autoplay playsinline></video>
  <video id="remoteVideo" autoplay playsinline></video>
  <script src="webrtc.js"></script>
</body>
</html>

JavaScript:

const localVideo = document.getElementById('localVideo');
const remoteVideo = document.getElementById('remoteVideo');
 
const configuration = {
  iceServers: [
    { urls: 'stun:stun.l.google.com:19302' }
  ]
};
const peerConnection = new RTCPeerConnection(configuration);
 
navigator.mediaDevices.getUserMedia({ video: true, audio: true })
  .then(stream => {
    localVideo.srcObject = stream;
    stream.getTracks().forEach(track => peerConnection.addTrack(track, stream));
  })
  .catch(error => {
    console.error('Error accessing media devices.', error);
  });
 
peerConnection.onicecandidate = event => {
  if (event.candidate) {
    sendCandidateToRemote(event.candidate);
  }
};
 
peerConnection.ontrack = event => {
  remoteVideo.srcObject = event.streams[0];
};
 
peerConnection.createOffer()
  .then(offer => {
    return peerConnection.setLocalDescription(offer);
  })
  .then(() => {
    sendOfferToRemote(peerConnection.localDescription);
  })
  .catch(error => {
    console.error('Error creating offer.', error);
  });
 
function handleRemoteOffer(offer) {
  peerConnection.setRemoteDescription(new RTCSessionDescription(offer))
    .then(() => {
      return peerConnection.createAnswer();
    })
    .then(answer => {
      return peerConnection.setLocalDescription(answer);
    })
    .then(() => {
      sendAnswerToRemote(peerConnection.localDescription);
    })
    .catch(error => {
      console.error('Error handling offer.', error);
    });
}
 
function handleRemoteAnswer(answer) {
  peerConnection.setRemoteDescription(new RTCSessionDescription(answer))
    .catch(error => {
      console.error('Error setting remote description.', error);
    });
}
 
function handleRemoteCandidate(candidate) {
  peerConnection.addIceCandidate(new RTCIceCandidate(candidate))
    .catch(error => {
      console.error('Error adding ICE candidate.', error);
    });
}
 
function sendOfferToRemote(offer) {
  // Implement this function to send the offer to the remote peer
}
 
function sendAnswerToRemote(answer) {
  // Implement this function to send the answer to the remote peer
}
 
function sendCandidateToRemote(candidate) {
  // Implement this function to send the candidate to the remote peer
}

9.7 安全性

WebRTC 使用多种安全协议来保护通信安全,包括:

  • SRTP:用于加密音视频流,防止窃听和篡改。
  • DTLS:用于加密数据通道和信令数据,确保数据完整性和保密性。
  • 同源策略:WebRTC 遵循浏览器的同源策略,防止跨站脚本攻击。

9.8 应用场景

WebRTC广泛应用于以下场景:

  • 视频会议:如 Zoom、Google Meet 等。
  • 在线教育:如实时教学、远程实验等。
  • 远程医疗:如医生与患者之间的视频咨询。
  • 社交媒体:如实时视频聊天和直播。
  • 游戏:如实时多人游戏中的语音聊天和数据同步。

10.XML、fetch、Beacon 、WebSocket、SSE和WebRTC的对比

10.1 用途与类型

  • XMLHttpRequest 和 XMLHttpRequest Level 2: 早期的AJAX技术,用于从服务器异步获取数据。Level 2是XMLHttpRequest的扩展,增加了更多功能。
  • Fetch API: 现代、基于Promise的API,用于网络请求,支持跨域。
  • Beacon API: 用于在不影响当前页面性能的情况下,发送数据到服务器。
  • WebSocket API: 提供全双工通信渠道,允许服务器与客户端之间的实时、双向通信。
  • Server-Sent Events (SSE): 允许服务器主动向客户端发送更新,基于HTTP协议,但只能单向通信。
  • WebRTC: 不是直接用于HTTP请求,而是用于浏览器之间的实时通信,支持视频、音频和数据共享。

10.2 同步性与实时性

  • XMLHttpRequest 和 XMLHttpRequest Level 2、Fetch API: 异步操作,但非实时通信(除非通过轮询)。
  • Beacon API: 异步,设计用于不阻塞页面的数据发送。
  • WebSocket API 和WebRTC: 提供实时双向通信。
  • SSE: 实时单向通信(从服务器到客户端)。

10.3 复杂度

  • XMLHttpRequest 和 XMLHttpRequest Level 2: 较为原始,需要处理较多细节。
  • Fetch API: 较为现代,使用Promise,但可能需要额外的库来处理复杂需求(如JSON解析)。
  • Beacon API: 非常简单,用于发送少量数据。
  • WebSocket API、SSE、WebRTC: 复杂度较高,需要处理网络事件和可能的连接问题。

10.4 兼容性

  • XMLHttpRequest 和 XMLHttpRequest Level 2: 广泛支持,几乎所有现代浏览器都支持。
  • Fetch API: 现代浏览器支持,但在一些旧版浏览器中可能需要polyfill。
  • Beacon API、WebSocket API、SSE、WebRTC: 大多数现代浏览器支持,但在某些旧浏览器上可能不可用。

10.5 使用场景

  • XMLHttpRequest 和 XMLHttpRequest Level 2、Fetch API: 适用于需要从服务器异步获取数据的情况。
  • Beacon API: 适用于发送分析数据、日志等,不影响页面性能。
  • WebSocket API: 实时游戏、聊天应用、实时通知等需要双向实时通信的场景。
  • SSE: 实时新闻更新、股票行情等需要从服务器实时获取数据但不需要发送数据的场景。
  • WebRTC: 视频会议、在线游戏等需要实时音视频通信的场景。

11.http常见的请求方式

11.1 简单说说http有哪些请求方式

HTTP/1.1协议中共定义了八种方法(有时也叫“动作”),来表明Request-URL指定的资源不同的操作方式。其中:HTTP1.0定义了三种请求方法: GET, POST 和 HEAD方法。HTTP1.1新增了五种请求方法:OPTIONS, PUT, DELETE, TRACE 和 CONNECT 方法

  • GET : 发送请求,获取服务器数据
  • POST :向URL指定的资源提交数据
  • PUT :向服务器提交数据,以修改数据
  • HEAD :请求页面的首部,获取资源的元信息。类似于GET请求,但返回的响应中没有具体内容,用于获取报头。
  • DELETE :删除服务器上的某些资源。
  • OPTIONS :获取当前URL支持的方法。如果请求成功,会有一个Allow头包含类似“GET,POST”的信息。(请求预处理)
  • CONNECT 将请求连接转换为透明的TCP/IP通道。
  • TRACE 用于激发一个远程的应用层请求消息回路。

11.2 get VS post

get用于请求获取数据;post则用于提交数据;

  • 请求参数的位置
    get的请求参数放在url上,多个参数用&连接;post的请求参数则放请求体中;
  • 请求长度限制
    get请求的参数受url长度的限制;post则不会,可以传输大数据
  • 安全性
    post相对比get更安全一些。但要想保证安全,还得依赖于SSL/TLS加密
  • 幂等性
    GET请求是幂等的,即多次执行同一GET请求,服务器将返回相同的结果。而POST请求则不是幂等的,因为每次提交都会创建新的资源。
  • 缓存
    GET请求可以被缓存,而POST请求则不会,除非在响应头中包含适当的Cache-Control或Expires字段。
  • 后退/刷新按钮的影响
    GET请求可以被浏览器缓存,因此可以通过点击后退按钮或刷新按钮来重复执行。而POST请求则不会,因为这些操作对POST请求没有实际意义。

幂等的请求:指的是同样的请求被执行一次和连续执行多次的效果是一样的,服务器的状态也是一样的。即如果一个Http请求在使用相同的数据对服务器进行相同的请求时。不会对服务器的运行状态造成任何的负担和阻塞

11.3 options(预检请求)

一个问题:有些http请求(如post请求)为什么会多发送一次option请求?
这个涉及到跨域请求,详情可见https://www.ruanyifeng.com/blog/2016/04/cors.html

比如当一个POST请求比较复杂时,传输一个POST请求比较消耗带宽及性能,若被拦截再返回,这样子十分消耗资源。于是可以采用更加轻量化的OPTIONS请求去预检。当OPTIONS请求出现问题时,将不再执行实际的POST请求,这样可以提升性能,特别是遇到跨域问题的时候。

12.http组成

http是基于tcp的应用层协议,它应用于Web浏览器和Web服务器之间的通信。当用户通过浏览器访问一个网站时,浏览器会向服务器发送HTTP请求,服务器则根据请求的内容返回相应的HTTP响应,包括请求的网页、图片、视频等资源。

12.1 请求报文

HTTP 请求报文由请求行、请求头、空行和请求包体(body)组成。如下图所示:
在这里插入图片描述
真实示例如下:

GET / HTTP/1.1
Host: www.baidu.com
Connection: keep-alive
Cache-Control: max-age=0
sec-ch-ua: " Not A;Brand";v=“99”, “Chromium”;v=“96”, “Google Chrome”;v=“96”
sec-ch-ua-mobile: ?0
sec-ch-ua-platform: “macOS”
Upgrade-Insecure-Requests: 1
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/96.0.4664.110 Safari/537.36
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,/;q=0.8,application/signed-exchange;v=b3;q=0.9
Sec-Fetch-Site: none
Sec-Fetch-Mode: navigate
Sec-Fetch-User: ?1
Sec-Fetch-Dest: document
Accept-Encoding: gzip, deflate, br
Accept-Language: zh-CN,zh;q=0.9,en;q=0.8
Cookie: BIDUPSID=8B0207CE0B6364E5934651E84F17999B; PSTM=1619707475;

12.1.1 请求行

主要描述了客户端想要如何操作服务端的资源;请求行由三部分构成:

  • 请求方法:表示对资源期望进行何种操作,常用的如 GET、POST
  • 请求目标:通常是一个 URL ,表明了要操作的资源。
  • 版本号:表示报文使用的 HTTP 协议版本。

这三个部分通常使用空格(space)来分隔,最后要用 CRLF 换行表示结束。

GET / HTTP/1.1

这个请求行,结合之前的描述,意思就是 “服务端妹子你好,我是客户端蛋蛋,现在我想获取网站根目录的默认信息,我这边用的协议版本是 1.1,麻烦你也要用这个版本回复我哦”

12.1.2 请求头

请求头(Request Header):客户端发送请求时,携带的一些额外的信息,例如Accept、Accept-Language、User-Agent等。

HTTP请求头由若干个键值对组成,每个键值对中的键表示请求头的名称,值表示请求头的值。
在这里插入图片描述

12.1.3 请求空行

空行(Blank Line):请求头和请求体之间需要一个空行。

请求空行主要是用于请求头和请求体进行分割的标识,不然无法区分哪部分是请求头哪部分是请求体。
在这里插入图片描述

12.1.4 请求体

请求体就是 HTTP 要传输的内容,HTTP 可以承载很多类型的数字数据:图片、音频、视频、HTML 文档等。

  • 介绍响应报文
  • 首部字段
  • 介绍功能

需要注意的是,不是所有的HTTP请求都需要请求体。例如,HTTP GET 请求不包含请求体,因为它们只需要从服务器获取数据,而不需要向服务器发送数据。而 HTTP POST 请求通常包含请求体,因为它们需要将数据传输到服务器。
在这里插入图片描述

12.2 响应报文

HTTP 响应报文由状态行、响应头部、空行和响应包体(body)组成。如下图所示:
在这里插入图片描述
以请求 http://www.baidu.com为例:

HTTP/1.1 200 OK
Bdpagetype: 1
Bdqid: 0xfb0d743100040ad2
Cache-Control: private
Connection: keep-alive
Content-Encoding: gzip
Content-Type: text/html;charset=utf-8
Date: Fri, 24 Dec 2021 08:20:44 GMT
Expires: Fri, 24 Dec 2021 08:20:44 GMT
Server: BWS/1.1
Set-Cookie: BDSVRTM=17; path=/
Set-Cookie: BD_HOME=1; path=/
Set-Cookie: H_PS_PSSID=35635_34439_35104_35628_35488_35436_35456_34584_35491_35584_35586_34873_35317_26350_35610_35562; path=/; domain=.baidu.com
Strict-Transport-Security: max-age=172800
Traceid: 1640334044050133761018090243032019634898
X-Frame-Options: sameorigin
X-Ua-Compatible: IE=Edge,chrome=1
Transfer-Encoding: chunked

12.2.1 响应行

响应行(Status Line):服务器响应的第一行,包括HTTP版本号、状态码和状态消息。
在这里插入图片描述

12.2.2 响应头

响应头(Response Header):服务器响应时,携带的一些额外信息,例如Content-Type、Content-Length、Server等。
在这里插入图片描述

12.2.3 响应空行

响应空行:用于分割响应头和响应体的标识
在这里插入图片描述

12.2.4 响应体

响应体(Response Body):服务器响应的实际内容,例如HTML文档、图片、JSON数据等。
在这里插入图片描述

13.http状态码

13.1 大致分类

  • 1开头表示消息
  • 2开头表示请求处理成功状态码
  • 3开头表示请求重定向
  • 4开头表示客户端错误
  • 5开头表示服务器错误

13.2 1xx

  • 100
    客户端继续发送请求。
  • 101
    服务器根据客户端的请求切换协议,用于websocket和http2

13.3 2xx

  • 200
    客户端发的请求被服务端正常处理
  • 201
    请求已经被实现,并因此创建了一个新的资源。
  • 202
    服务接受请求但还没处理
  • 204
    服务器成功处理了请求,但没有返回任何内容。

13.4 3xx

  • 301(永久重定向)
    请求的资源已经被永久地移动到新的url上。服务器将会把新的url地址放在响应头中给客户端,然后浏览器会自动发新请求给新地址
  • 302(临时重定向)
    和301差别不大,地址暂时被修改了,之后可能还会被修改成其它地址或者改为原地址。与301不同的是:在浏览器中301会有缓存,当发生了一次永久重定向之后,再次访问该地址,浏览器请求的地址直接是缓存中的新地址;302不会有缓存,每次发送地址时候,都会返回302,并且在响应头添加新地址,然后浏览器自动请求新地址。
  • 304文件内容未被修改
    告诉客户端有缓存,直接用缓存中的内容

13.5 4xx

  • 400
    客户端请求的语法有误,服务端无法处理
  • 401(Unauthorized)
    未授权,请求要求进行身份验证。比如需要登录的页面可能会响应该状态;
  • 403
    服务端拒绝访问资源。
  • 404
    资源不存在

13.6 5xx

  • 500
    服务器内部错误
  • 503
    服务器停机维护/超负载,无法处理请求
  • 505
    服务器不支持,或者拒绝支持在请求中使用的 HTTP 版本

13.7 状态码301和302?

13.7.1 共同点

301和302状态码都表示重定向,就是说浏览器在拿到服务器返回的这个状态码后会自动跳转到一个新的URL地址,这个地址可以从响应的Location首部中获取(用户看到的效果就是他输入的地址A瞬间变成了另一个地址B)。

13.7.2 不同点

‌对搜索引擎的影响‌:

  • 301‌:搜索引擎会将权重和排名从原始URL转移到新的URL,并将旧的URL替换为重定向后的新URL。
  • 302‌:搜索引擎会抓取新的内容,但不会更新链接的权重和排名,旧的URL仍然有效。‌

‌对浏览器缓存的影响‌:

  • 301‌:浏览器会自动记住新的URL,并直接请求新的URL,原始URL的请求会被重定向到新URL
  • 302‌:浏览器不会缓存这个重定向,原始URL的请求仍然会指向原来的位置。

使用场景‌:

  • 301‌:适用于资源已经永久移动的情况,例如网站迁移或域名变更。
  • 302‌:适用于临时性的资源移动,例如维护或内容更新。

13.7.3 应用实例

在实际应用中,301和302重定向的实现可以通过服务器配置HTTP响应头中的Location字段来实现。例如,在Express.js框架中,可以通过res.redirect()方法来实现重定向,指定状态码(301或302)和目标URL。此外,这两种重定向方式各有其特点和适用场景,正确运用它们可以有效维护网站的可访问性和用户体验,但需要注意避免过度使用302重定向,以免对网站的SEO产生负面影响‌。

下面用express为例:
由a链接触发重定向:

//app.js
const express = require('express')
const app = express()
const router = express.Router()

 // 演示通过页面跳转的方式进行重定向,无需前端操作
 router.get('/api/passToPageRedirect', (req, res) => {
    // 这里的 res.redirect(响应状态码可以是301,302, 要重定向的地址)
   res.redirect(302, 'http://localhost:3000/liuyan.html')
    res.end()
})

app.listen(3000, ()=> {
  console.log('http://localhost:3000')
})

前端代码如下:

<a href="/api/passToPageRedirect">跳转</a>
<form action="/api/passToPageRedirect" method="get">
    <button type='submit'>表单触发页面跳转</button>
</form>

a链接和表单的提交,触发的默认行为,都会刷新页面,都能进行重定向操作。在触发301或者 302重定向以后,页面刷新,控制台中的 network面板中,可以看到请求的过程:
在这里插入图片描述

而此时的 页面地址,已经是liuyan.html:
在这里插入图片描述

通过接口触发重定向:
如果调用GET、或者POST接口,然后后台进行重定向操作,其实express只会给你在响应头加一个Location字段,前端必须拿到Location 然后,自己通过window.location.href = Location地址跳转:

//app.js
const express = require('express')
const app = express()
const router = express.Router()

// 使用Location进行重定向
 router.get('/api/passToAPIRedirect', (req, res) => {
    res.location('http://localhost:3000/liuyan.html')
    res.end()
  })

app.listen(3000, ()=> {
  console.log('http://localhost:3000')
})

前端代码:

<script src="/plugins/jquery.js"></script>
  <script>
    $.ajax({
      url: '/api/passToAPIRedirect',
      success(res, stauts, xhr) {
        console.log(xhr.getAllResponseHeaders())
        window.location.href = xhr.getResponseHeader('Location')
      },
      error(err) {
        console.log(err)
      }
    })
  </script>

在success响应函数体内,我们可以获取到服务器返回的响应头部添加的属性 Location:
在这里插入图片描述
使用window.location.href=Location地址 手动调用一下,我们是不会在看到跳转以后的liuyan.html页面的控制台看到/api/passToAPIRedirect接口的详细信息,因为已经没了,页面刷新了。

总结:

  • 使用 301、302重定向,要页面自己使用a,form表单触发默认行为
  • 使用location手动进行页面跳转,需要前端自己获取响应头中的Location字段值,然后调用 window.location.href跳转
  • 2种方式,在控制台中看到的效果是不同的,window.location.href在跳转以后,看不到请求接口的详细信息, 301,302的依旧可以在liuyan.html目标页面中看的到信息

14.https协议

14.1 https为什么比http安全?

  • 通信使用明文(不加密),内容可能会被盗用
    HTTPS使用SSL/TLS协议对HTTP报文进行加密,使得敏感数据在网络传输过程中不容易被窃听和篡改。这种加密过程结合了对称加密和非对称加密,确保数据的保密性和完整性。
  • 不验证通信方的身份,因此有可能遭遇伪装
    HTTPS通过数字证书进行身份验证,确保通信双方的真实性。在建立HTTPS连接时,服务器会提供数字证书来证明自己的身份。如果验证通过,客户端就可以信任服务器,并继续与其进行安全的数据传输。这有效防止了被恶意伪装的服务器攻击。
  • 无法证明报文的完整性,所以有可能已遭篡改
    在传输数据之前,HTTPS会对数据进行加密,并使用消息摘要(hash)算法生成一个摘要值。在数据到达接收端后,接收端会使用相同的算法对接收到的数据进行摘要计算,并与发送端的摘要值进行比较。如果两者一致,说明数据在传输过程中没有被篡改。如果不一致,通信双方应重新进行验证或中断连接。
    https=http+SSL/TLS证书

总结起来,https相对http有以下特点:

  • 信息加密
  • 身份验证
  • 完整性校验

14.2 关于SSL和TLS

14.2.1 SSL和TLS分别是什么

SSL 是最早的安全协议,而 TLS 是在 SSL 的基础上发展起来的。它们都提供了加密和认证机制,用于确保数据传输的机密性和完整性。

TLS 和 SSL 主要用于以下两个方面:

  • 加密通信:TLS/SSL 使用加密算法来对数据进行加密,防止第三方截获和窃听通信内容。它可以确保数据在传输过程中的隐私性。
  • 身份认证:TLS/SSL 还提供了身份验证机制,用于确认通信双方的身份,并确保数据只发送到正确的接收方。这可以防止恶意用户冒充其他用户或服务器。

注意:
TLS在运输层之上,TCP,UDP协议并不安全,TLS用于解决安全问题。SSL是TLS的一个弃用的版本。

在这里插入图片描述
TLS内部用到了对称加密、非对称加密和散列函数。这三种方法都用了,所以比较安全。

14.2.2 对称加密和非对称加密

(1)对称加密
常见的算法有AESDES加密
举例: 麒麟->星月发消息 但是他们的消息不想被别人知道,采用了对称加密,于是他们两个协商了一段密钥,今生永相随
麒麟:AES算法 + 密钥(今生永相随)+明文(吃面) = XMZSXMZS==
星月:使用AES + 密钥(今生永相随)+密文( XMZSXMZS==) = 吃面

总结: 发送方和接收方使用同一个密钥(一串字符串)进行加密和解密

  • 服务端使用密钥进行加密
  • 客户端使用密钥进行解密

但是第一次要传输一次密钥,如果密钥被拦截,就被破解了。这种方式性能好,速度快,缺点,密钥被拦截被破解

(2)非对称加密
常见算法有RSADSA 加密。
举例: 麒麟->星月发消息,这次使用的是非对称加密,生成了公钥和私钥,公钥可以对外公开,私钥必须只能麒麟知道不能泄露
星月:RSA + 公钥 + 明文(吃面) = XMZS==
麒麟:RSA + 私钥 + 密文(XMZS==) = 吃面

总结: 一个公钥,一个私钥。公钥加密,私钥解密

  • 服务端(私钥)
  • 客户端(公钥)
  • 客户端公钥加密传输(被拦截无法解密,需要用私钥)
  • 服务端通过私钥解密

这种方法优点是比较安全,确定缺点是性能差,耗时间。

14.2.3 TLS的使用方法

详细见此处。这里是一种模拟方法,也可以自己去某云购买证书,按照相应的说明书安装。

14.3 关于https

在这里插入图片描述
总结一下:https使用非对称加密进行密钥传输,使用对称加密进行数据传输。 下面详细说一下HTTPS的非对称加密流程(密钥传输),因为数据传输是使用“非对称加密”得到的密钥来进行对称加密的得到的,这个就不用多说(如图中5)

  • 服务端生成公钥和私钥
    ○ 服务端启动时,会生成一对公钥和私钥。
    ○ 公钥和私钥是成对的,但它们的值是不同的。公钥可以公开,任何人都可以使用公钥加密数据。而只有私钥的拥有者才能解密使用公钥加密的数据。
  • 服务端将公钥发送给客户端
    ○ 当客户端需要与服务器建立安全连接时,服务器会将公钥发送给客户端。
    ○ 客户端接收到公钥后,会将其存储起来,以备后续的数据传输使用。
  • 客户端生成对称加密的密钥
    ○ 客户端使用服务端发送的公钥进行加密,生成一个对称加密的密钥。
    ○ 这个密钥只会在客户端和服务端之间共享,不会公开。
  • 客户端向服务端发送对称加密的密钥
    ○ 客户端将对称加密的密钥使用服务端的公钥进行加密,然后将其发送给服务端。
    ○ 服务端收到加密后的密钥后,使用自己的私钥进行解密,得到对称加密的密钥。
  • 数据传输
    ○ 在之后的通信中,客户端和服务端都会使用这个对称加密的密钥对数据进行加密和解密。
    ○ 由于对称加密的密钥只有客户端和服务端知道,所以保证了数据的安全性。

如何保证首次传输的公钥是安全的?
保证公钥的安全性是HTTPS非对称加密中非常重要的一环。实际上,在HTTPS中,公钥的发送并不直接通过明文传输,而是通过CA证书进行加密和验证。

CA证书是由权威的第三方机构颁发的,用于验证服务器身份的证书。它包含了服务器的公钥和其他相关信息,如服务器的域名、证书颁发机构等。当客户端访问服务器时,服务器会将CA证书发送给客户端,同时使用CA证书中的公钥对服务器的公钥进行加密。

客户端会使用自己存储的CA证书中的公钥对服务器发送过来的公钥进行解密,得到服务器的公钥。同时,客户端还会验证CA证书的签名和有效期等信息,以确保接收到的公钥是来自合法且有效的服务器。

通过这种方式,即使有人截获了服务器发送给客户端的公钥,也无法解密得到真正的公钥,因为只有持有私钥的CA才能对公钥进行解密。同时,如果客户端验证发现CA证书是无效的或者过期的,它将不会信任该服务器,从而保证了公钥的安全性。

因此,保证第一步发送给服务端的公钥是安全的关键在于使用可靠的CA证书来加密和验证公钥。

15.JWT

该部分内容我在nodejs中详细讲解过。详情见这儿的第五和第六节。

16.网络状态

16.1 online和offline事件

online 和 offline 事件是浏览器自带的两个事件,可以通过添加事件监听器来检测当前网络连接状态。当浏览器的网络连接发生变化,比如从在线状态切换到离线状态,或者从离线状态切换到在线状态时,这两个事件就会被触发。以下是示例代码:

window.addEventListener('online', () => {
  console.log('Online');
});

window.addEventListener('offline', () => {
  console.log('Offline');
});

16.2 navigator.onLine

除了使用事件监听器之外,JavaScript 还提供了另一种方式来检测浏览器的网络连接状态,即使用 navigator.onLine 属性。该属性返回一个布尔值,表示浏览器是否处于联网状态。以下是示例代码:

if (navigator.onLine) {
  console.log('Online');
} else {
  console.log('Offline');
}

在上述代码中,我们使用了 navigator.onLine 属性来检测当前的网络连接状态,并根据返回的布尔值输出相应信息到控制台。需要注意的是,navigator.onLine 属性只能检测当前的网络连接状态,而不能监听网络连接状态的变化。如何通过前端获取更多的网络信息,这就需要navigator.connection。

16.3 navigator.connection

navigator.connection 是 Web API 中提供的一种获取网络连接相关信息的接口。该接口返回的是一个 NetworkInformation 对象,包含了多个关于用户设备网络连接状况的属性,如网络类型、带宽、往返时间等。
通过 navigator.connection API 能够获取的主要网络连接属性如下:

  • downlink: 当前网络连接的估计下行速度(单位为 Mbps)
  • downlinkMax: 设备网络连接最大可能下行速度(单位为 Mbps)
  • effectiveType: 当前网络连接的估计速度类型(如 slow-2g、2g、3g、4g 等)
  • rtt: 当前网络连接的估计往返时间(单位为毫秒)
  • saveData: 是否处于数据节省模式

除此之外,还有其他诸如 type、onchange 等属性,用于获取设备网络连接的类型和注册网络连接状态变化事件等功能。

下面是代码示例:

if ('connection' in navigator) {
  const networkInfo = navigator.connection;
  console.log('Network downlink:', networkInfo.downlink);
  console.log('Network effective type:', networkInfo.effectiveType);
  console.log('Network round-trip time:', networkInfo.rtt);
  console.log('Network data saving mode:', networkInfo.saveData);
} else {
  console.log('navigator.connection is not supported.');
}

navigator.connection API 是一种用于获取用户设备当前的网络连接状态信息的 JavaScript 接口,能够提供多种有助于优化网络应用程序的关键性能指标,如带宽、往返时间等。通过该 API 您可以针对用户当前的网络环境进行优化,从而提高应用程序的性能和用户体验。值得注意的是,由于不同浏览器兼容性问题的存在,需要在使用前进行检测以保证代码正常运行。

17.DNS域名解析

17.1 是什么

是一种基于UDP的协议。域名解析系统。把域名和ip做了一个映射(把域名翻译成IP地址)。

17.2 域名

域名从上到下分为根域名、顶级域名、二级域名、三级域名…

举个例子https://www.xxx.com
www是三级域名;xxx是二级域名,com是顶级域名。系统为用户做了兼容性,域名末尾的根域名.可以不用输入。

在域名的每一层都有一个域名服务器。如下:
在这里插入图片描述

17.3 域名的查询方式

17.3.1 递归查询

在这里插入图片描述

17.3.2 迭代查询

在这里插入图片描述

17.4 域名缓存

计算机中DNS的记录有两种缓存方式:

  • 浏览器缓存
    浏览器在获取网站域名的实际IP地址后会对其进行缓存,减少网络请求的损耗;
  • 操作系统缓存
    操作系统缓存其实是用户自己配置的hosts文件

17.5 查询过程

在这里插入图片描述

  • 首先搜索浏览器的DNS缓存,缓存中维护一张域名和ip地址的对照表;
  • 若没有命中,继续搜索操作系统的DNS缓存以及hosts文件;
  • 若没有命中,操作系统会将域名发给本地域名服务器,本地域名服务器查询自己的DNS缓存,查询成功就返回结果
  • 若本地域名的DNS没有查询成功,则会向根域名服务器发送请求,根域名服务器会根据请求的域名数据来返回顶级域名服务器的地址给本地服务器;
  • 本地域名服务器拿到顶级域名服务器的地址之后,会向顶级域名服务器发送请求,获取权威(权限)域名服务器的地址;
  • 本地域名服务器拿到权威(权限)域名服务器的地址之后,会向权威(权限)域名服务器发生请求,得到该域名最终的ip地址;
  • 本地域名服务器将获取的IP地址返回给操作系统,同时自己将IP地址缓存起来;
  • 操作系统将IP地址返回给浏览器,同时自己将IP地址缓存起来;
  • 至此,浏览器就得到了该域名对应的IP地址,吧将ip地址缓存起来。

注意:主机到本地dns是递归查询,本地dns后面的查询是迭代查询

18.http缓存机制

http有强缓存和弱缓存(也叫协商缓存)两种。
在这里插入图片描述

18.1 强缓存

强缓存是浏览器本地根据服务器设置的过期时间来判断是否使用缓存,未过期则从本地缓存里拿资源,已过期则重新请求服务器获取最新资源。

18.1.1 Expires

这是HTTP/1.0中引入的概念。浏览器请求某资源,服务器发送响应数据给浏览器.在浏览器中响应头有个字段Expires‌,它指定了一个过期时间,这个时间是GMT格式的绝对时间。

Expires: Wed, 21 Oct 2015 07:28:00 GMT

在这里插入图片描述

当浏览器再次请求该资源时,如果当前时间在Expires之前,浏览器就会直接使用本地缓存的资源,而不会向服务器发送请求。然而,Expires是基于服务器时间计算的,浏览器的时间可以自己更改!!!

18.1.2 Cache-Control

这是HTTP/1.1中引入的更灵活的缓存控制方式,通过设置max-age属性来指定资源在缓存中的存活时间。
与Expires不同,Cache-Control是基于客户端时间来计算的,因此更可靠。Cache-Control可以设置资源的缓存策略,如public、private、no-cache、no-store等,以及指定资源的最大存活时间max-age。这些设置使得Cache-Control比Expires更加细致和可控。

在这里插入图片描述

注意:max-age 优先级要大于 Expires

18.2 协商缓存

协商缓存则是浏览器本地每次都向服务器发起请求,由服务器来告诉浏览器是从缓存里拿资源还是返回最新资源给浏览器使用。

18.2.1 Last-Modified/If-Modified-Since

这是HTTP/1.0中引入的概念
服务器在响应头中返回资源的最后修改时间(Last-Modified),客户端在再次请求资源时,会带上If-Modified-Since请求头,其值为上一次获取资源时的修改时间。服务器根据这个时间判断资源是否已被修改,如果资源未被修改,则返回304状态码,通知客户端直接使用本地缓存;如果资源已被修改,则返回新的资源内容。

18.2.2 ETag/If-None-Match

这是HTTP/1.1中引入的概念。‌
ETag是服务器在响应头中返回资源的唯一标识符,客户端在再次请求资源时,会带上If-None-Match请求头,其值为上一次获取资源时的标识符。服务器比较两个标识符是否一致,如果一致,表示资源未被修改,返回304状态码;如果不一致,表示资源已被修改,返回新的资源内容。

18.3 总结

在这里插入图片描述
注意:强缓存优先级比协商缓存更高。当强缓存开启并且在有效期内,则不会走协商缓存。

19.http1.0/http1.1/http2.0

19.1 http版本

HTTP协议有多个版本,包括HTTP/0.9、‌HTTP/1.0,HTTP/1.1和HTTP/2.0

19.2 HTTP/0.9

这是第一个HTTP草案,只有一个GET方法,不支持头部或状态代码,唯一可用的数据格式是HTML。

19.3 HTTP/1.0

  • 短连接
    浏览器和服务器之间只保持短暂的连接。浏览器的每次请求都需要与服务器建立一个tcp连接。 服务器完成请求处理后会立即断开TCP连接,服务器也不会跟踪每个用户也不会记录过去的请求。
  • 单一请求响应
    一次TCP连接在同一时间内只能处理一个请求,下一个请求必须在前一个请求响应完毕才能发送。
  • 头部简陋
    指的是他没有明确规定缓存限制,身份验证及其它一些常见的头部字段。

下面举一个例子:
在这里插入图片描述
例如,解析html文件,当发现文件中存在资源文件的,这时候又创建单独的链接。最终导致,一个html文件的访问包含了多次的请求和响应,每次请求都需要创建连接、关系连接这种形式明显造成了性能上的缺陷。
如果需要建立长连接,需要设置一个非标准的Connection字段----Connection: keep-alive

在这里插入图片描述

19.4 HTTP/1.1

19.4.1 http1.0和http1.1的对比图

下面来看http1.0和http1.1的对比图:
在这里插入图片描述
http1.0不支持分片上传和断点上传;http1.1支持分片上传和断点上传。

19.4.2 http1.1的特点

  • 引入了持久连接
    tcp连接默认不关闭,可以被多个请求复用(请求复用);
  • 管道机制
    在一个TCP连接中,可以同时发送多个请求,但服务器还是会按照顺序进行响应。假如客户端要发送两个请求。之前做法是:在同一个TCP连接中,先发送A请求,得等服务器做出响应,收到后才能发送B请求。而现在的管道机制则是:在同一个TCP请求中,浏览器可以同时发送A和B请求,但服务器还是会按照顺序进行响应,先回应A请求,完成后在回应B请求。
  • 新增了请求方法,请求头和响应头

19.4.3 http1.1的队头堵塞问题

(1)队头堵塞问题
虽然1.1允许请求复用,并且可以同时发送多个请求,但服务器必须按顺序处理。如果前面某个回应特别慢,后面就会有很多请求排队等着,这被称为“队头堵塞”。

(2)队头堵塞原因
队头阻塞与短连接/长连接无关,而是由HTTP基本的”请求-应答“模式所导致的:

  • 因为HTTP规定报文必须”一发一收“,这就形成了一共先进先出的串行队列。队列里的请求没有轻重缓急的优先级,只有入队的先后顺序,排在最前面的请求被最先处理。
  • 如果队首的请求因为处理的太慢耽误了时间,那么队列里后面的所有请求也不得不跟着一起等待,结果就是其他的请求承担了不应有的时间成本。

19.4.4 http1.1的队头堵塞问题的缓解方法

因为“请求-应答”模型不能变,所以“队头阻塞”的问题在HTTP/1.1里面无法解决,只能缓解:

(1)并发连接
并发连接是同时对一个域名发起多个长连接,用数量来解决质量的问题。

  • 缺点是如果每个客户端都想自己快,建立很多个连接,用户数并发数就会是个天文数字。服务器的资源根本就杠不住,或者被服务器认为是恶意连接,反而会造成“拒绝服务”。
  • 所以,HTTP协议建议客户端使用并发,但不能“滥用”并发
    • RFC2616 里明确限制每个客户端最多并发 2 个连接。
    • 不过实践证明这个数字实在是太小了,众多浏览器都“无视”标准,把这个上限提高到了 6~8。后来修订的RFC7230 也就“顺水推舟”,取消了这个“2”的限制。

”并发连接“ VS “高并发请求”

  • “高并发请求”是服务器端的概念,意思是同时有多个客户端连接服务器。
  • “并发连接”是客户端的概念,意思是一个浏览器并发多个连接,访问服务器。

(2)域名分片
但“并发连接”所压榨出的性能也跟不上高速发展的互联网无止境的需求,还有什么别的办法吗?

  • 公司发展的太快了,员工越来越多,上下班打卡成了迫在眉睫的大问题。前台空间有限,放不下更多的打卡机了,怎么办?那就多开几个打卡的地方,每个楼层、办公区的入口也放上三四台打卡机,把人进一步分流,不要都往前台挤。
  • 这个就是“域名分片”(domain sharding)技术,还是用数量来解决质量的思路。
  • HTTP 协议和浏览器不是限制并发连接数量吗?好,那我就多开几个域名,比如 shard1.chrono.comshard2.chrono.com,而这些域名都指向同一台服务器www.chrono.com,这样实际长连接的数量就又上去了。

19.5 HTTP/2.0

升级到HTTP2.0,必须是基于HTTPS的基础上,一般都是通过nginx配置完成。具体过程可见这里。也可自己网上搜索。

19.5.1 HTTP/2.0的特点

(1)多路复用:
HTTP/2复用tcp连接,在一个连接中,浏览器和客户端都可以同时发送多个请求和回应,并且不用按照顺序一一对应,这样子就避免了队头阻塞问题。

(2)二进制分帧:
帧是http2通信中最小单位信息。
http2采用二进制格式传输数据,而非http1.x的文本格式,解析起来更高效。将请求和啊应数据分割为更小的帧,并且它们采用二进制编码。
http2中同域名下所有通信都在单个连接上完成,该连接可以承载任意数量的双向数据流。每个数据流都以消息的形式发送,而消息又由一个或多个帧组成。多个帧之间可以乱序发送,根据帧首部的流标可以 重新组装,这也是多路复用同时发送数据的实现条件。

(3)首部压缩:
http2在客户端和服务器端使用首部表来跟踪和存储之前发送的键值对,对于相同的数据,不再通过每次请求和响应发送。

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

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

在这里插入图片描述
(4)服务器推送:
允许服务器推送资源给客户端:服务器会顺便把一些客户端需要的资源一起推送给客户端,如在一个页面请求中,就可以随同页面的其它资源,免得客户端需要再次创建连接发送到服务器获取,这种方式十分适合加载静态资源。
在这里插入图片描述

19.5.2 HTTP/2.0的缺点

流水的http铁打的tcp;http2解决了应用层的队头阻塞问题,但没有解决传输层的队头阻塞问题

19.6 HTTP/3

基于UDP协议。实际上是基于udp协议重写了tcp功能,与tcp功能差不多。
在这里插入图片描述
http3解决了两个问题:

  • 三次握手四次挥手;
  • 传输层的队头阻塞问题

19.7 如何查看http的版本

一般项目默认都是使用http1.1(没配置时候)。

在浏览器右键检查,Network里面,如下图所示,右击红色边框栏,则弹出要显示的菜单项。Protocol对应的就是Http的协议版本。
在这里插入图片描述

19.8 总结

19.8.1 http1.0

  • 短连接
  • 单一请求响应
  • 头部简陋

19.8.2 http1.1

  • 引入了持久连接
  • 管道机制
  • 新增了请求方法,请求头和响应头

19.8.3 http2.0

  • 多路复用
  • 二进制分帧
  • 头部压缩
  • 服务器推送

19.8.4 http3.0

  • 基于udp重写了udp
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值