简易的 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');
})

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/413873.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

计算多图的等价无向图的邻接链表表示

计算多图的等价无向图的邻接链表表示 摘要:一、引言二、算法思路三、伪代码实现四、C代码实现五、算法分析六、结论摘要: 在图论中,多图(Multigraph)是一种允许边重复以及存在自循环边(即一个顶点到其自身的边)的图。给定一个多图的邻接链表表示,本文旨在探讨如何构造…

PHP软件下载-安装-环境配置

.1.下载 下载地址如下 windows.php.net - /downloads/releases/ 安装包如下. .2.安装 可以在D盘或者E盘的根目录创建一个自定义目录。注意文件夹目录中不能包含中文&#xff0c;不能包含空格等特殊字符。 版本说明&#xff1a; (1)ts表示非线程安全版本。这个安装包还指明了…

c++模拟实现数据结构之vector篇

那么本篇文章是带大家一起实现一下数据结构vector&#xff0c;那么我们现在就进入正题。 目录 接口介绍部分 增加 尾插 指定插入与头插 删除 尾删 指定位置删除 主要代码逻辑 增加 尾插 指定插入与头插 删除 尾删 指定位置删除 一些其他接口的代码逻辑 模拟实现…

django企业开发实战-学习小结

写在前面 初次阅读此书是三年前&#xff0c;当时没经历过完整的项目 觉得这书就是扯淡 后来经历过项目加班与毒打 今天再翻开此书 觉得实乃不可多得之物 花些时间啃下来吧 django版本 3.2 本博客开源项目地址 kimsmith/django企业实战 (gitee.com) 有的代码因为版本混乱报错…

Unity 3D学习资料集合

本文包含了unity3D 游戏开发相关的学习资料&#xff0c;包含了入门、进阶、性能优化、面试和书籍等学习资料&#xff0c;含金量非常高&#xff0c;在这里分享给大家&#xff0c;欢迎收藏。 学习社区 1.Unity3D开发者 Unity3D开发者论坛是一个专注于Unity引擎的开发者社区。在这…

VSCode设置复制 Ctrl+D想下复制

VSCode 默认向下复制当前行是 shift Alt ↓,但是我们习惯了IDE和webStrom的CtrlD的想下复制.下面是VSCode自定义快捷键. VSCode设置复制 CtrlD想下复制 1.文件->首选项->键盘快捷方式(ctrk 在案ctrs)2.输入 copy line down->右键->更改键绑定3.完成 1.文件->首…

探索《黑神话:悟空》背后的编程技术

《黑神话&#xff1a;悟空》作为一款备受期待的动作角色扮演游戏&#xff0c;以其卓越的视觉效果和流畅的游戏体验吸引了全球玩家的关注。这款游戏不仅在艺术设计和技术实现上展现了极高的水准&#xff0c;其背后的编程技术更是保证了游戏顺利运行和出色表现的关键因素。在这篇…

低代码技术新趋势——逆向工程

低代码的下一个趋势&#xff0c;应该是“逆向工程”&#xff0c;用户可以通过 可视化界面&#xff0c;逆向输出全栈工程代码。而标准的工程代码同样可以编译为支持可视化分析、编辑、调整的“无代码”程序。前一个是解释性语言向编译性语言的逆向工程。后者则是一个理论实践应用…

如何成为一个飞控算法工程师?

兄弟&#xff0c;这个问题问得好&#xff0c;但也别想着靠看几本书就能一步登天。飞控算法这玩意儿&#xff0c;真要干好了&#xff0c;不是简简单单几个公式几个库就能搞定的。你本科电子专业有点基础&#xff0c;玩过四轴飞行器也算是入门了&#xff0c;但要搞真算法&#xf…

ComfyUI:基于差分扩散的像素级图像修改

在几个月的沉寂之后&#xff0c;差分扩散&#xff08;Differential Diffusion&#xff09;被引入了。 玩了几天之后&#xff0c;我仍然对结果感到震惊。 这种新的先进方法允许以像素为基础进行更改&#xff0c;而不是以整个区域为基础进行更改。 另一种可能更通俗的说法&…

PCL-直通滤波

本篇内容&#xff1a; 讲解直通滤波的作用通过pcl实现直通滤波 效果&#xff1a; 1 主要原理 点云数据通常包含x、y、z三个维度的数据&#xff0c;用户指定维度、范围后&#xff0c;直通滤波过滤或保留该范围内的所有点云 假设我指定维度’y’&#xff0c;范围&#xff08;…

【unity实战】使用新版输入系统Input System+Rigidbody实现第三人称人物控制器

最终效果 前言 使用CharacterController实现3d角色控制器&#xff0c;之前已经做过很多了&#xff1a; 【unity小技巧】unity最完美的CharacterController 3d角色控制器&#xff0c;实现移动、跳跃、下蹲、奔跑、上下坡、物理碰撞效果&#xff0c;复制粘贴即用 【unity实战】C…

Ubuntu 24.04部署Wordpress

环境&#xff1a; Ubuntu 24.04 PHP 8.1.2-1ubuntu2.18 Nginx/1.18.0 (Ubuntu) WordPress 6.6.1 Mysql 8 文章目录 1. 安装php2. 配置nginx2.1. 安装nginx2.2. 配置 3. 下载wordpress3.1. 配置wordpress 4. mysql配置wordpress数据库和用户4.1. 安装和远程连接4.2. 创建wordpre…

【论文笔记】独属于CV的注意力机制CBAM-Convolutional Block Attention Module

目录 写在前面 一、基数和宽度 二、通道注意力模块&#xff08;Channel Attention Module&#xff09; 三、空间注意力模块&#xff08;Spatial Attention Module&#xff09; 四、CBAM&#xff08;Convolutional Block Attention Module&#xff09; 五、总结 写在前面 …

Photomator 3.3.22 (macOS Universal) - 照片编辑软件

Photomator 3.3.22 (macOS Universal) - 照片编辑软件 适用于 Mac、iPhone 和 iPad 的终极照片编辑器 请访问原文链接&#xff1a;https://sysin.org/blog/photomator/&#xff0c;查看最新版。原创作品&#xff0c;转载请保留出处。 作者主页&#xff1a;sysin.org Photoma…

LeetCode_sql_day20(1398.购买了产品A和产品B却没有购买产品C的顾客)

描述: Customers 表&#xff1a; ------------------------------ | Column Name | Type | ------------------------------ | customer_id | int | | customer_name | varchar | ------------------------------ customer_id 是这张表中具有唯一…

浏览器播放RTSP流,支持H264、H265等格式,支持IE、Chrome等浏览器

目录 背景 解决方案 效果 代码 前端代码 后端代码 下载 背景 项目中需要在浏览器中播放RTSP流&#xff0c;实在是不想折腾ActiveX控件 1、麻烦&#xff08;开发麻烦、使用时设置也麻烦&#xff09; 2、非IE浏览器不兼容 解决方案 使用OpenCvSharpNancy写一个解码服…

617. 合并二叉树

目录 一&#xff1a;题目&#xff1a; 二&#xff1a;代码&#xff1a; 三&#xff1a;结果&#xff1a; 一&#xff1a;题目&#xff1a; 给你两棵二叉树&#xff1a; root1 和 root2 。 想象一下&#xff0c;当你将其中一棵覆盖到另一棵之上时&#xff0c;两棵树上的一些…

捷达千里江山首发亮相,捷达品牌2024成都车展继续宠粉不停

2024年8月30日&#xff0c;捷达品牌携新车捷达千里江山惊艳亮相2024成都国际车展&#xff0c;并在五周年之际&#xff0c;发布幸福包油计划等宠粉福利&#xff0c;号召用户打卡千里江山&#xff0c;奔赴美好。与此同时&#xff0c;全新捷达VS5/VS7五周年纪念版车型进一步降低了…

H264码流结构讲解

所谓的码流结构就是指&#xff1a;视频经过编码之后所得到的数据是怎样排列的&#xff0c;换句话说&#xff0c;就是编码后的码流我们该如何将一帧一帧的数据分离开来&#xff0c;哪一块数据是一帧图像&#xff0c;哪一块是另外一帧图像&#xff0c;只要了解了这个&#xff0c;…