Websocket
WebSocket是什么
WebScoket是一种在单个TCP连接上进行的全双工通信的协议。
WebScoket使得客户端和服务器之间的数据交换变得更加简单,允许服务器主动向客户端推送数据,在Websocket API 中,浏览器和服务器只需要完成一次握手,两者之间可以创建持久的连接,并进行双向数据传输
HTTP和WS之间的区别
1、http协议:客户端向服务端发送一次请求,服务端响应返回一次数据,服务端不会主动返回客户端
2、ws协议:只需要建立一次连接,服务端与客户端之间可以互通数据,服务端可以主动响应客户端
WebSocket的基本使用
nodejs
//创建一个express server,安装ws
npm i ws
完整代码实现通信传输
1.服务端
//server/index.js
var express = require('express');
var router = express.Router();
// 引入ws模块
const webSocket = require('ws').Server;
const port = 3001;
// 创建websocket服务端
const server = new webSocket({port}, () => {
console.log('websocket服务端启动成功');
});
const connectHandler = (ws) => {
console.log('建立连接');
// 监听客户端出错
ws.on('error', errorHandler);
// 监听客户端断开连接
ws.on('close',closeHandler);
// 监听客户端发送消息
ws.on('message', messageHandler);
}
const errorHandler = (error) => {
console.log('客户端出错了');
}
const closeHandler = (e) => {
console.log('客户端关闭了');
}
function messageHandler(data){
console.log('接收到了客户端发送的消息:',JSON.parse(data));
this.send(JSON.stringify(JSON.parse(data)))
}
// 建立连接
server.on('connection', connectHandler)
/* GET home page. */
router.get('/', function(req, res, next) {
res.render('index', { title: 'Express' });
});
module.exports = router;
2.客户端
//App.vue
<script setup>
// 实例化
let ws = new WebSocket('ws://localhost:3001')
// open
ws.onopen = () => {
//客户端给服务端发送的数据
ws.send('6666566')
console.log('连接成功');
}
// message
ws.onmessage = (e) => {
// 服务端给客户端发送的data
console.log('收到消息', e.data);
}
// close
ws.onclose = () => {
//连接关闭了
console.log('连接关闭');
}
// error
ws.onerror = (e) => {
console.log('连接错误', e);
}
</script>
注:JSON.stringify
是将对象值转换成JSON字符串 , JSON.parse
是将字符串转换成对象
实现利用webscoket实现前后端数据传输的简单操作,建立了websocket连接,下面是服务端和客户端分别接收到的信息:
Websocket的心跳机制和断线重连
为什么需要心跳和重连呢?
WebSocket 连接可能因各种原因断开,但客户端和服务器不一定能及时感知:
- 网络波动:临时中断,可能自动恢复
- 防火墙超时:长时间无数据传输会被防火墙切断
- 服务器重启:服务端主动断开连接
心跳机制:定期发送测试消息,确认连接是否正常 重连机制:检测到断开后,自动尝试重新建立连接
心跳机制和重连机制的实现:
基于刚才写的demo,现在写个简易版心跳机制的实现,原理:
客户端定期(如30s)发送一个ping
消息
服务器收到后立刻回复pong
消息
客户端设置超时(如5s),若没有收到pong
消息则认为连接断开
重连机制关键点:
- 在onclose和onerror事件中触发重连
- 创建新的websocket实例并重新绑定all事件(重载函数)
改进后的代码:添加了心跳监测和重连
//App.vue
<script setup>
// 实例化
let ws = new WebSocket('ws://localhost:3001')
let heartbeatTimer = null
let reconnectTimer = null
let isConnected = false // 连接状态标志
// 启动心跳
const startHeartBeat = () => {
if(heartbeatTimer) clearInterval(heartbeatTimer)
heartbeatTimer = setInterval( () => {
if(ws.readyState === WebSocket.OPEN){
console.log('发送心跳ping');
ws.send(JSON.stringify({ModeCode : 'heart_beat'}))
}
},30000) //30s发送一次心跳
}
// open
ws.onopen = () => {
ws.send('6666566')
isConnected = true;
startHeartBeat(); //连接成功后启动心跳
console.log('连接成功');
}
// message
ws.onmessage = (e) => {
// 服务端给客户端发送的data
console.log('收到消息', e.data);
const data = JSON.parse(e.data)
//处理心跳响应
if(data.ModeCode === 'heart_beat'){
console.log('收到心跳响应 pong');
}
}
// close
ws.onclose = () => {
console.log('连接关闭');
isConnected = false
clearInterval(heartbeatTimer)
//尝试重连
if(!reconnectTimer){
reconnectTimer = setTimeout(() => {
reconnect();
},3000) //3s后重连
}
}
// error
ws.onerror = (e) => {
console.log('连接错误', e);
//错误时也尝试重连
ws.close() //确保关闭连接触发重连
}
// 重连函数
const reconnect = () => {
console.log('尝试重连...')
clearTimeout(reconnectTimer)
reconnectTimer = null
// 创建新的WebSocket实例
ws = new WebSocket('ws://localhost:3001')
// 重新绑定事件
ws.onopen = () => {
console.log('重连成功')
isConnected = true
startHeartBeat()
ws.send('6666566')
}
ws.onmessage = (e) => {
console.log('收到消息', e.data)
const data = JSON.parse(e.data)
if (data.ModeCode === 'heart_beat') {
console.log('收到心跳响应 pong')
}
}
ws.onclose = () => {
console.log('重连后又断开')
isConnected = false
clearInterval(heartbeatTimer)
if (!reconnectTimer) {
reconnectTimer = setTimeout(() => {
reconnect()
}, 3000)
}
}
ws.onerror = (e) => {
console.log('重连错误', e)
ws.close()
}
}
</script>
服务端代码:关键是
//server/index.js
var express = require('express');
var router = express.Router();
// 引入ws模块
const webSocket = require('ws').Server;
const port = 3001;
// 创建websocket服务端
const server = new webSocket({port}, () => {
console.log('websocket服务端启动成功');
});
const connectHandler = (ws) => {
console.log('建立连接');
// 监听客户端出错
ws.on('error', errorHandler);
// 监听客户端断开连接
ws.on('close',closeHandler);
// 监听客户端发送消息
ws.on('message', messageHandler);
}
const errorHandler = (error) => {
console.log('客户端出错了');
}
const closeHandler = (e) => {
console.log('客户端关闭了');
}
function messageHandler(data){
console.log('接收到了客户端发送的消息:', data.toString());
try {
const parsedData = JSON.parse(data);
const { ModeCode } = parsedData;
switch (ModeCode){
case 'message':
console.log('收到普通消息');
break;
case 'heart_beat':
console.log('心跳监测');
// 直接回复pong,避免重复解析和序列化
this.send(JSON.stringify({ ModeCode: 'heart_beat', status: 'pong' }));
break;
}
} catch (error) {
console.error('解析消息失败:', error);
}
}
// 建立连接
server.on('connection', connectHandler)
/* GET home page. */
router.get('/', function(req, res, next) {
res.render('index', { title: 'Express' });
});
module.exports = router;
这样就实现了简易的心跳机制和重连机制:
综上所述,WebSocket 凭借其全双工通信的特性,为前后端实时通信提供了便利。而心跳机制和重连机制的引入,进一步增强了 WebSocket 连接的稳定性和可靠性,使得在复杂的网络环境下,也能保证通信的顺畅进行。