简易的 Websocket + 心跳机制 + 尝试重连

2024-08-23 21:04

本文主要是介绍简易的 Websocket + 心跳机制 + 尝试重连,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

文章目录

  • 演示
  • 大纲
  • 基础 WebSocket
  • 前端: 添加心跳机制
  • 前端: 尝试重新连接
  • 历史代码


还没有写完,bug 是有的,我在想解决办法了…

演示

请添加图片描述


大纲

  • 基础的 webSocket 连接
  • 前后端:添加心跳机制
  • 后端无心跳反应,前端尝试重新连接
  • 设置重新连接次数,超过最大尝试次数之后,不再尝试重新连接

基础 WebSocket

前端的基础就是这些,大概的效果是这样的

请添加图片描述

<body><button onclick="reConnect()">1. 重建连接</button><button onclick="sendMessage()">2. 发消息</button><button onclick="stopConnect()">3. 断开连接</button>
</body>
<script>let ws = null // 使用null来标记当前没有活动的 WebSocket 连接function createNewWebSocket() {if (ws && ws.readyState !== WebSocket.CLOSED) {ws.close() // 确保关闭旧的连接}ws = new WebSocket('ws://localhost:8080')ws.onopen = function (evt) {console.log('Connection open ...')}ws.onmessage = function (evt) {console.log('Received Message: ' + evt.data)}ws.onclose = function (evt) {console.log('Connection closed.')}}function sendMessage() {if (ws) ws.send(`前端发送:>> ${new Date()}`)}function stopConnect() {if (ws) ws.close()}function reConnect() {createNewWebSocket()}
</script>

后端的代码基本不变,所以我直接把心跳也做好
后端的心跳就是:拿到前端的值,如果是 ping 的话,就返回一个 pong,其他逻辑保持不变

const http = require('http')
const WebSocket = require('ws')
const server = http.createServer()
const wss = new WebSocket.Server({ server })wss.on('connection', (socket) => {console.log('webSocket 连接成功')socket.on('message', (message) => {// 将 Buffer 转换为字符串const messageStr = message.toString()const currentRandom = Math.random()const isSendPong = currentRandom < 0.5console.log('后端收到消息:>>' + messageStr)// 检查是否为心跳请求if (messageStr === 'ping') {socket.send(`当前随机值为 ${currentRandom}, 是否发送心跳:${isSendPong}`)//  50%的概率发送 "pong"if (isSendPong) {socket.send('pong') // 心跳响应}} else {const message = `后端发送消息:>> 你好前端~ ${new Date().toLocaleString()}`socket.send(message)}})socket.on('close', () => {console.log('websocket 已经关闭')})
})server.on('request', (request, response) => {response.writeHead(200, { 'Content-Type': 'text/plain' })response.end('Hello, World')
})server.listen(8080, () => {console.log('服务器已启动,端口号为 8080')
})

前端: 添加心跳机制

思路:前端写一个定时器,用于隔一段时间发送一个 ping
效果如下图所示请添加图片描述

好吧,false 的概率有点高,不顾可以看历史记录

我在后端设置了随机逻辑,模拟一下出错的场景,百分之50的概率回应前端的心跳,如果 true 的话,后端就回应前端的心跳,返回 pong

前端 代码如下

<body><button onclick="reConnect()">1. 重建连接</button><button onclick="sendMessage()">2. 发消息</button><button onclick="stopConnect()">3. 断开连接</button>
</body>
<script>let ws = null // 使用null来标记当前没有活动的 WebSocket 连接let heartbeatTimer = null // 心跳定时器const HEARTBEAT_INTERVAL = 5000 // 每隔 5 秒发送一次心跳function createNewWebSocket() {if (ws && ws.readyState !== WebSocket.CLOSED) {ws.close() // 确保关闭旧的连接}ws = new WebSocket('ws://localhost:8080')ws.onopen = function (evt) {console.log('Connection open ...')startHeartbeat()}ws.onmessage = function (evt) {console.log('Received Message: ' + evt.data)handleHeartbeatResponse(evt.data)}ws.onclose = function (evt) {console.log('Connection closed.')stopHeartbeat()}}function sendMessage() {if (ws) ws.send(`前端发送:>> ${new Date().toLocaleString()}`)}function stopConnect() {if (ws) ws.close()stopHeartbeat()}function reConnect() {createNewWebSocket()}function startHeartbeat() {heartbeatTimer = setInterval(() => {ws.send('ping')}, HEARTBEAT_INTERVAL)}function stopHeartbeat() {clearInterval(heartbeatTimer)heartbeatTimer = null}function handleHeartbeatResponse(message) {if (message === 'heartbeat') {console.log('Heartbeat received.')clearTimeout(heartbeatTimer) // 清除超时定时器startHeartbeat() // 重新启动心跳}}
</script>

前端: 尝试重新连接

设置一个场景: 前端发送三个心跳包,如果都没有反应,那么就判断为断开连接了,就去重新连接

历史代码

前端

<!DOCTYPE html>
<html lang="en"><head><meta charset="UTF-8" /><meta name="viewport" content="width=device-width, initial-scale=1.0" /><title>Document</title></head><body><button id="btn">发消息</button><button id="stop">断链</button><button id="reconnect">重新连接</button></body><script>const btn = document.querySelector('#btn')const stop = document.querySelector('#stop')const reconnect = document.querySelector('#reconnect')let ws = null // 使用null来标记当前没有活动的WebSocket连接let heartbeatInterval = null // 存储心跳定时器let timeoutId = null // 存储超时定时器let reconnectAttempts = 0 // 重连尝试次数const maxReconnectAttempts = 3 // 最大重连次数function createNewWebSocket() {if (ws && ws.readyState !== WebSocket.CLOSED) {ws.close() // 确保关闭旧的连接}ws = new WebSocket('ws://localhost:8080')ws.onopen = function (evt) {console.log('Connection open ...')startHeartbeat() // 开始发送心跳}ws.onmessage = function (evt) {console.log('Received Message: ' + evt.data)// 检查是否为心跳响应if (evt.data === 'pong') {clearTimeout(timeoutId) // 清除超时定时器resetHeartbeatTimer() // 重置心跳定时器}}ws.onclose = function (evt) {console.log('Connection closed.')clearInterval(heartbeatInterval) // 清除心跳定时器reconnectWebSocket() // 尝试重新连接}}// 发送心跳包function sendHeartbeat() {if (ws) {ws.send('ping')timeoutId = setTimeout(() => {// 如果在超时时间内没有收到 "pong" 响应,则关闭当前连接console.log('超时,关闭连接')ws.close()}, 15000) // 设置超时时间为 15 秒}}// 启动心跳function startHeartbeat() {sendHeartbeat() // 立即发送第一个心跳包heartbeatInterval = setInterval(sendHeartbeat, 30000) // 每30秒发送一次}// 重置心跳定时器function resetHeartbeatTimer() {clearInterval(heartbeatInterval)heartbeatInterval = setInterval(sendHeartbeat, 30000) // 重新设置定时器}// 重新连接function reconnectWebSocket() {console.log('尝试重新连接', reconnectAttempts)// 检查是否超过最大重连次数if (reconnectAttempts < maxReconnectAttempts) {reconnectAttempts++createNewWebSocket()} else {console.log('超过最大重连次数,不再尝试连接')}}btn.addEventListener('click', () => {if (ws) {ws.send(`前端发送:>> ${new Date()}`)}})stop.addEventListener('click', () => {if (ws) {ws.close()}})reconnect.addEventListener('click', () => {createNewWebSocket()})</script>
</html>

后端

const http = require('http')
const WebSocket = require('ws')
const server = http.createServer()
const wss = new WebSocket.Server({ server })wss.on('connection', (socket) => {console.log('webSocket 连接成功')socket.on('message', (message) => {// 将 Buffer 转换为字符串const messageStr = message.toString();console.log('后端收到消息:>>' + messageStr);// 检查是否为心跳请求if (messageStr === 'ping') {socket.send('pong'); // 心跳响应} else {socket.send('后端发送消息:>> hello 我是 socket.send');}})socket.on('close', () => {console.log('websocket 已经关闭');})
})server.on('request', (request, response) => {response.writeHead(200, { 'Content-Type': 'text/plain' });response.end('Hello, World');
})server.listen(8080, () => {console.log('服务器已启动,端口号为 8080');
})

这篇关于简易的 Websocket + 心跳机制 + 尝试重连的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/1100486

相关文章

Maven 配置中的 <mirror>绕过 HTTP 阻断机制的方法

《Maven配置中的<mirror>绕过HTTP阻断机制的方法》:本文主要介绍Maven配置中的<mirror>绕过HTTP阻断机制的方法,本文给大家分享问题原因及解决方案,感兴趣的朋友一... 目录一、问题场景:升级 Maven 后构建失败二、解决方案:通过 <mirror> 配置覆盖默认行为1. 配置示

Redis过期删除机制与内存淘汰策略的解析指南

《Redis过期删除机制与内存淘汰策略的解析指南》在使用Redis构建缓存系统时,很多开发者只设置了EXPIRE但却忽略了背后Redis的过期删除机制与内存淘汰策略,下面小编就来和大家详细介绍一下... 目录1、简述2、Redis http://www.chinasem.cn的过期删除策略(Key Expir

Go语言中Recover机制的使用

《Go语言中Recover机制的使用》Go语言的recover机制通过defer函数捕获panic,实现异常恢复与程序稳定性,具有一定的参考价值,感兴趣的可以了解一下... 目录引言Recover 的基本概念基本代码示例简单的 Recover 示例嵌套函数中的 Recover项目场景中的应用Web 服务器中

Jvm sandbox mock机制的实践过程

《Jvmsandboxmock机制的实践过程》:本文主要介绍Jvmsandboxmock机制的实践过程,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录一、背景二、定义一个损坏的钟1、 Springboot工程中创建一个Clock类2、 添加一个Controller

Dubbo之SPI机制的实现原理和优势分析

《Dubbo之SPI机制的实现原理和优势分析》:本文主要介绍Dubbo之SPI机制的实现原理和优势,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录Dubbo中SPI机制的实现原理和优势JDK 中的 SPI 机制解析Dubbo 中的 SPI 机制解析总结Dubbo中

Java 的 Condition 接口与等待通知机制详解

《Java的Condition接口与等待通知机制详解》在Java并发编程里,实现线程间的协作与同步是极为关键的任务,本文将深入探究Condition接口及其背后的等待通知机制,感兴趣的朋友一起看... 目录一、引言二、Condition 接口概述2.1 基本概念2.2 与 Object 类等待通知方法的区别

嵌入式Linux驱动中的异步通知机制详解

《嵌入式Linux驱动中的异步通知机制详解》:本文主要介绍嵌入式Linux驱动中的异步通知机制,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录前言一、异步通知的核心概念1. 什么是异步通知2. 异步通知的关键组件二、异步通知的实现原理三、代码示例分析1. 设备结构

JVM垃圾回收机制之GC解读

《JVM垃圾回收机制之GC解读》:本文主要介绍JVM垃圾回收机制之GC,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录一、死亡对象的判断算法1.1 引用计数算法1.2 可达性分析算法二、垃圾回收算法2.1 标记-清除算法2.2 复制算法2.3 标记-整理算法2.4

C++如何通过Qt反射机制实现数据类序列化

《C++如何通过Qt反射机制实现数据类序列化》在C++工程中经常需要使用数据类,并对数据类进行存储、打印、调试等操作,所以本文就来聊聊C++如何通过Qt反射机制实现数据类序列化吧... 目录设计预期设计思路代码实现使用方法在 C++ 工程中经常需要使用数据类,并对数据类进行存储、打印、调试等操作。由于数据类

SpringRetry重试机制之@Retryable注解与重试策略详解

《SpringRetry重试机制之@Retryable注解与重试策略详解》本文将详细介绍SpringRetry的重试机制,特别是@Retryable注解的使用及各种重试策略的配置,帮助开发者构建更加健... 目录引言一、SpringRetry基础知识二、启用SpringRetry三、@Retryable注解