WebSocket 心跳与重连机制:打造永不掉线的实时通信系统

0 阅读4分钟

Websocket

WebSocket是什么

WebScoket是一种在单个TCP连接上进行的全双工通信的协议。

WebScoket使得客户端和服务器之间的数据交换变得更加简单,允许服务器主动向客户端推送数据,在Websocket API 中,浏览器和服务器只需要完成一次握手,两者之间可以创建持久的连接,并进行双向数据传输

HTTP和WS之间的区别

1、http协议:客户端向服务端发送一次请求,服务端响应返回一次数据,服务端不会主动返回客户端

http.png

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连接,下面是服务端和客户端分别接收到的信息:

client.png

server.png

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;

这样就实现了简易的心跳机制和重连机制:

心跳机制服务端.png

心跳机制客户端.png

综上所述,WebSocket 凭借其全双工通信的特性,为前后端实时通信提供了便利。而心跳机制和重连机制的引入,进一步增强了 WebSocket 连接的稳定性和可靠性,使得在复杂的网络环境下,也能保证通信的顺畅进行。