本篇文章着重讲述了Http、WebSocket与SSE三种通讯方式的特点与实现方式以及它们之间的区别。
代码实现
function startPolling(url, interval=5000) { setInterval(async () => { try { const response = await fetch(url); if (!response.ok) { throw new Error(`HTTP error! Status: ${response.status}`); } const data = await response.json(); console.log(data); } catch (error) { console.error(error); } }, interval); } // 使用方法: startPolling('https://api.example.com/data', 5000); // 每5秒轮询一次
这段代码定义了一个startPolling函数,它接收一个URL和轮询间隔(以毫秒为单位,默认5000毫秒)。使用setInterval来周期性地发送HTTP请求到指定的URL,并在收到响应后打印结果。如果发生错误,会在控制台中记录错误信息。
WebSocket是一种在单个TCP连接上进行全双工通信的协议。
webSocket使得客户端和服务器之间的数据交换变得更加简单,允许服务端主动向客户端推送数据。在WebSocket API中,浏览器和服务器只需要完成一次握手,两者之间就直接可以创建持久性的连接,并进行双向数据传输。
代码实现
前端
nodejs(服务器)完整代码
var express = require('express'); var router = express.Router(); //引入ws模块 const WebSocket = require('ws').Server; const port = 3001; //创建服务 const server = new WebSocket({ port }, () => { console.log('webSocket服务开启'); }) const errorHandler = (error) => { console.log('errorHandler====>客户端出错了', error) } const closeHandler = (e) => { console.log('closeHandler====>客户端断开了', e) } function messageHandler(data) { console.log('messageHandler====>接收客户端消息', JSON.parse(data)) this.send(JSON.stringify(JSON.parse(data))) } const connectHandler = (ws) => { console.log('客户端已连接'); //监听客户端出错 ws.on('error', errorHandler); //监听客户端断开链接 ws.on('close', closeHandler); //监听客户端发来的消息 ws.on('message', messageHandler); } //建立连接 server.on('connection', connectHandler); /* GET home page. */ router.get('/', function(req, res, next) { res.render('index', { title: 'Express' }); }); module.exports = router;
除了最基本的实现外,WebSocket还具有心跳检测机制与断线重连机制。
具体实现代码
前端
// Socket.js class Socket { wsUrl: string = ""; constructor(url: string) { this.wsUrl = url; } ModeCode = { MSG: "message", // 普通消息 HEART_BETA: "heart_beta", // 心跳检测 }; ws: WebSocket; webSocketState: boolean = false; // webSocket连接状态 heartBeta = { time: 5 * 1000, //心跳检测间隔时间 timeout: 3 * 1000, //心跳超时间隔 reconnection: 10 * 1000, //断线重连时间 }; reconnectTimer; // 断线重连时间器 connectWebSocket() { this.ws = new WebSocket(this.wsUrl); this.init(); } // 初始化函数 init() { this.ws.addEventListener("open", () => { //ws状态设置为连接:true this.webSocketState = true; //是否启动心跳检测 this.heartBeta?.time ? this.startHeartBeta(this.heartBeta.time) : ""; console.log("开启连接"); }); this.ws.addEventListener('message', (e) => { const data = JSON.parse(e.data); switch (data.ModeCode) { case this.ModeCode.MSG: console.log("普通消息"); break; case this.ModeCode.HEART_BETA: this.webSocketState = true; console.log("心跳消息", data.msg); break; } }) this.ws.addEventListener('error', (error) => { this.webSocketState = false }) this.ws.addEventListener("close", (e) => { this.webSocketState = false; }); } // 心跳检测初始化 startHeartBeta(time: number) { setTimeout(() => { this.ws.send( JSON.stringify({ ModeCode: this.ModeCode, msg: new Date() }) ); this.waitingServer(); }, time); } // 延时等待服务端响应,通过webSocketState判断是否连线成功 waitingServer() { this.webSocketState = false; setTimeout(() => { if (this.webSocketState) { this.startHeartBeta(this.heartBeta.time); } console.log("心跳无响应,已经断线了"); try { this.ws.close(); } catch (e) { console.log("连接已关闭"); } this.reconnectWebSocket(); }, this.heartBeta.timeout); } // 重新连接 reconnectWebSocket() { this.reconnectTimer = setTimeout(() => { this.reconnectWs(); }, this.heartBeta.timeout); } reconnectWs() { if (!this.ws) { // 第一次执行,初始化 this.connectWebSocket() } if (this.ws && this.reconnectTimer) { // 防止多个webSocket同时执行 clearTimeout(this.reconnectTimer) this.reconnectTimer = null; this.reconnectWebSocket() } } } export default Socket;
后端 node.js
var express = require('express'); var router = express.Router(); //引入ws模块 const WebSocket = require('ws').Server; const port = 3001; //创建服务 const server = new WebSocket({ port }, () => { console.log('webSocket服务开启'); }) const errorHandler = (error) => { console.log('errorHandler====>客户端出错了', error) } const closeHandler = (e) => { console.log('closeHandler====>客户端断开了', e) } function messageHandler(data) { console.log('messageHandler====>接收客户端消息', JSON.parse(data)) // this.send(JSON.stringify(JSON.parse(data))) const { ModeCode } = JSON.parse(data) switch (ModeCode) { case 'message': console.log('收到前端的消息'); break; case 'heart_beta': console.log('心跳检测'); this.send(JSON.stringify(JSON.parse(data))); break default: break; } } const connectHandler = (ws) => { console.log('客户端已连接'); //监听客户端出错 ws.on('error', errorHandler); //监听客户端断开链接 ws.on('close', closeHandler); //监听客户端发来的消息 ws.on('message', messageHandler); } //建立连接 server.on('connection', connectHandler); /* GET home page. */ router.get('/', function(req, res, next) { res.render('index', { title: 'Express' }); }); module.exports = router;
Server-Sent Events(SSE)是一种用于实现服务器向客户端实时推送数据的Web技术。与传统的轮询和长轮询相比,SSE提供了更高效和实时的数据推送机制。严格地说,HTTP 协议是无法做到服务器主动推送信息。但是,有一种变通方法,那就是SSE。
SSE基于HTTP协议,允许服务器将数据以事件流(Event Stream)的形式发送给客户端。客户端通过建立持久的HTTP连接,并监听事件流,可以实时接收服务器推送的数据。
SSE与WebSocket 作用相似,但是WebSocket更强大和灵活。因为WebSocket是全双工通道,可以双向通信;SSE 是单向通道,只能服务器向浏览器发送。
和WebSocket相比SSE优点:
代码实现:
SSE数据:
- {{ item.data }}
SSE与Http短轮训和WebSocket之间的区别就是其他两种通讯方式是双向的,而SSE则是服务端向客服端传输数据(单向的),例如ChatGPT。而短轮询相比WebSocket则适用于对实时性要求不高、数据更新频率较低的场景,例如一些简单的数据展示页面。WebSocket则适用于对实时性要求较高,需要频繁传输大量数据的场景,例如在线聊天、实时数据更新等。