前端必备工具推荐网站(免费图床、API和ChatAI等实用工具):
http://luckycola.com.cn/
WebSocket 是一种在单个 TCP 连接上进行全双工通信的网络协议。
它是 HTML5 中的一种新特性,能够实现 Web 应用程序和服务器之间的实时通信,比如在线聊天、游戏、数据可视化等。
相较于 HTTP 协议的请求-响应模式,使用 WebSocket 可以建立持久连接,允许服务器主动向客户端推送数据,避免了不必要的轮询请求,提高了实时性和效率。同时,WebSocket 的连接过程也比较简单,可以通过 JavaScript 中的 WebSocket API 进行创建和管理,并且可以和现有的 Web 技术如 HTML、CSS 和 JavaScript 无缝集成。
WebSocket 协议是基于握手协议(Handshake Protocol)的,它在建立连接时使用 HTTP/HTTPS 发送一个初始握手请求,然后服务器响应该请求,建立连接后就可以在连接上进行数据传输了。
在实现websocket连线过程中,需要通过浏览器发出websocket连线请求,然后服务器发出回应,这个过程通常称为“握手” 。
在 WebSocket API,浏览器和服务器只需要做一个握手的动作,然后,浏览器和服务器之间就形成了一条快速通道。两者之间就直接可以数据互相传送。在此WebSocket 协议中,为我们实现即时服务带来了两大好处:
1、WebSocket是一种在单个TCP连接上进行全双工通信的协议
2、WebSocket使得客户端和服务器之间的数据交换变得更加简单,允许服务端主动向客户端推送数据
3、在WebSocket API中,浏览器和服务器只需要完成一次握手,两者之间就直接可以创建持久性的连接,并进行双向数据传输
以前客户端想知道服务端的处理进度,要不停地使用 Ajax 进行轮询,让浏览器隔个几秒就向服务器发一次请求,这对服务器压力较高。另外一种轮询就是采用 long poll 的方式,这就跟打电话差不多,没收到消息就一直不挂电话,也就是说,客户端发起连接后,如果没消息,就一直不返回 Response 给客户端,连接阶段一直是阻塞的。
而 WebSocket 解决了 HTTP 的这几个难题。首先,当服务器完成协议升级后( HTTP -> WebSocket ),服务端可以主动推送信息给客户端,解决了轮询造成的同步延迟问题。由于 WebSocket 只需要一次 HTTP 握手,服务端就能一直与客户端保持通讯,直到关闭连接,这样就解决了服务器需要反复解析 HTTP 协议,减少了资源的开销。
WebSocket 是一种标准协议,用于在客户端和服务端之间进行双向数据传输。
但它跟 HTTP 没什么关系,它是一种基于 TCP 的一种独立实现。
定义:为了保持NebSocket稳定的长连接,在连接建立之后,服务器和客户端之间通过心跳包来保持连接态,以防止连接因为长时间没有数据传输而被切断。
let WS = connectWS();
let heartbeatStatus = 'waiting';
WS.addEventListener('open', () => {
// 启动成功后开启心跳检测
startHeartbeat()
})
WS.addEventListener('message', (event) => {
const { data } = event;
console.log('心跳应答了,要把状态改为已收到应答', data);
if (data === '"heartbeat"') {
heartbeatStatus = 'received';
}
})
function startHeartbeat() {
setTimeout(() => {
// 将状态改为等待应答,并发送心跳包
heartbeatStatus = 'waiting';
WS.send('heartbeat');
// 启动定时任务来检测刚才服务器有没有应答
waitHeartbeat();
}, 1500)
}
function waitHeartbeat() {
setTimeout(() => {
console.log('检测服务器有没有应答过心跳包,当前状态', heartbeatStatus);
if (heartbeatStatus === 'waiting') {
// 心跳应答超时
WS.close();
} else {
// 启动下一轮心跳检测
startHeartbeat();
}
}, 1500)
}
对于旧的浏览器该如何实现WebSocket的功能呢?下面就介绍一下几种常见的解决方案:
SockJS是一个JavaScript库,它为浏览器提供了一个类似WebSocket的对象。
首先,它会优先使用原生的WebSocket;如果不支持,则使用streaming;如果streaming也不支持,则使用轮询(polling)。下面是支持的浏览器概览:
既然模拟WebSocket双向通信,那么使用SockJS时,也要配合使用相应的服务器端的库,下面可以使用的服务器端库:
1、首先加载SockJS库
<script src="https://cdn.jsdelivr.net/npm/sockjs-client@1/dist/sockjs.min.js"></script>
2、初始化
var sock = new SockJS('https://mydomain.com/my_prefix');
sock.onopen = function() {
console.log('open');
sock.send('test');
};
sock.onmessage = function(e) {
console.log('message', e.data);
sock.close();
};
sock.onclose = function() {
console.log('close');
};
1、首先,安装sockjs-node:
npm install sockjs
2、初始化并且监听连接
var http = require('http');
var sockjs = require('sockjs');
var echo = sockjs.createServer({ sockjs_url: 'http://cdn.jsdelivr.net/sockjs/1.0.1/sockjs.min.js' });
echo.on('connection', function(conn) {
conn.on('data', function(message) {
conn.write(message);
});
conn.on('close', function() {});
});
var server = http.createServer();
echo.installHandlers(server, {prefix:'/echo'});
server.listen(9999, '0.0.0.0');
客户端和服务端的通信方式有很多种,大多数场景下都是由客户端主动发送数据给服务端,但在特定的场景下(如多人协作、在线游戏)客户端还需要和服务端保持实时通信,此时需要使用双向通信。
常见的双向通信方式包括 HTTP 短轮询(polling)、HTTP 长轮询(long-polling)、XHR Streaming、Server-Sent Events、Websocket 等。
客户端每隔特定的时间(比如 1s)便向服务端发起请求,获取最新的资源信息。该方式会造成较多的资源浪费,尤其当服务端内容更新频率低于轮询间隔时,就会造成服务端资源、客户端资源的浪费。除此之外,过于频繁的请求也会给服务端造成额外的压力,当服务端负载较高的时候,甚至可能导致雪崩等情况发生。
解决了短轮询的一些问题,长轮询实现特点主要为当客户端向服务端发起请求后,服务端保持住连接,当数据更新响应之后才断开连接。然后客户端会重新建立连接,并继续等待新数据。此技术的主要问题在于,在重新连接过程中,页面上的数据可能会过时且不准确。
可以维护客户端和服务端之间的连接。但使用 XHR Streaming 过程中,XMLHttpRequest对象的数量将不断增长,因此在使用过程中需要定期关闭连接,来清除缓冲区。
iframe 流方式是在页面中插入一个隐藏的 iframe,利用其 src 属性在服务器和客户端之间创建一条长连接,服务器向 iframe 传输数据(通常是 HTML,内有负责插入信息的 java),来实时更新页面。
它实现了浏览器与服务端全双工通信。前面我们提到,HTTP 短轮询、长轮询都会带来额外的资源浪费,因此 Websocket 在实现实时通信的同时,能更好地节省服务端资源和带宽。
Websocket 建立在 TCP 协议之上,握手阶段采用 HTTP 协议,但这个 HTTP 协议的请求头中,有以下的标识性内容。
Connection: Upgrade、Upgrade: websocket:表示这个连接将要被转换为 WebSocket 连接。
Sec-WebSocket-Key:向服务端提供所需的信息,以确认客户端有权请求升级到 WebSocket。
Sec-WebSocket-Protocol:指定一个或多个的 WebSocket 协议。
Sec-WebSocket-Version:指定 WebSocket 的协议版本。
如果服务端同意启动 WebSocket 连接,会在握手过程中的 HTTP 协议中返回包含Sec-WebSocket-Accept的响应消息,接下来客户端和服务端便建立 WebSocket 连接,并通过 WebSocket 协议传输数据。
在海量并发和客户端与服务器交互负载流量大的情况下,极大的节省了网络带宽资源的消耗,有明显的性能优势,且客户端发送和接受消息是在同一个持久连接上发起,实时性优势明显。
扫码登录实现
SSE(Server-Sent Events)是一种用于实现服务器主动向客户端推送数据的技术,也被称为“事件流”(Event Stream)。它基于 HTTP 协议,利用了其长连接特性,在客户端与服务器之间建立一条持久化连接,并通过这条连接实现服务器向客户端的实时数据推送。
SSE 基于 HTTP 协议,利用了其长连接特性,通过浏览器向服务器发送一个 HTTP 请求,建立一条持久化的连接。而 WebSocket 则是通过特殊的升级协议(HTTP/1.1 Upgrade 或者 HTTP/2)建立新的 TCP 连接,与传统 HTTP 连接不同。
SSE 可以传输文本和二进制格式的数据,但只支持单向数据流,即只能由服务器向客户端推送数据。WebSocket 支持双向数据流,客户端和服务器可以互相发送消息,并且没有消息大小限制。
适用于场景
chatGPT 返回的数据 就是使用的SSE 技术
实时数据大屏 如果只是需要展示 实时的数据可以使用SSE技术 而不是非要使用webSocket
前端调用
const sse = new EventSource('http://localhost:3000/api/sse' )
sse.addEventListener('open', (e) => {
console.log(e.target)
})
//对应后端nodejs自定义的事件名lol
sse.addEventListener('lol', (e) => {
console.log(e.data)
})
nodejs 后端操作
import express from 'express';
const app = express();
app.get('/api/sse', (req, res) => {
res.writeHead(200, {
'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
}
//返回自定义事件名
res.write(`event:lol\n`)
/返回数据
res.write(`data:${data.split('')[current]}\n\n`)
current++
}, 300)
})
app.listen(3000, () => {
console.log('Listening on port 3000');
});