WebSocket连接状态监控与自动重连实现

WebSocket连接状态监控与自动重连实现

下面我将实现一个具有连接状态监控和自动重连功能的WebSocket聊天室界面。

设计思路

  1. 创建直观的连接状态指示器
  2. 实现自动重连机制,包括:
    • 指数退避策略(重连间隔逐渐增加)
    • 最大重连次数限制
    • 手动重连按钮
  3. 添加心跳检测机制确保连接活跃
  4. 提供清晰的用户反馈

完整代码实现

<!DOCTYPE html>
<html lang="zh-CN">
<head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>WebSocket聊天室 - 带自动重连</title><style>* {box-sizing: border-box;margin: 0;padding: 0;}body {font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;line-height: 1.6;color: #333;background: linear-gradient(135deg, #f5f7fa 0%, #c3cfe2 100%);min-height: 100vh;padding: 20px;display: flex;justify-content: center;align-items: center;}.container {width: 100%;max-width: 800px;background: white;border-radius: 12px;box-shadow: 0 10px 30px rgba(0, 0, 0, 0.15);overflow: hidden;}header {background: #4a6ee0;color: white;padding: 20px;text-align: center;position: relative;}h1 {font-size: 24px;margin-bottom: 5px;}.subtitle {font-size: 14px;opacity: 0.9;}.status-bar {display: flex;justify-content: space-between;align-items: center;padding: 12px 20px;background: #f0f4ff;border-bottom: 1px solid #e0e6ff;}.status {display: flex;align-items: center;gap: 10px;}.status-indicator {width: 12px;height: 12px;border-radius: 50%;}.status.connected .status-indicator {background: #4caf50;box-shadow: 0 0 8px #4caf50;}.status.connecting .status-indicator {background: #ff9800;animation: pulse 1.5s infinite;}.status.disconnected .status-indicator {background: #f44336;}.reconnect-info {font-size: 13px;color: #666;margin-left: 15px;}.chat-container {padding: 20px;height: 350px;overflow-y: auto;background: #fafbff;display: flex;flex-direction: column;}.message {margin-bottom: 15px;padding: 10px 15px;border-radius: 18px;max-width: 80%;word-break: break-word;position: relative;}.received {background: #e6eeff;border-top-left-radius: 4px;align-self: flex-start;}.sent {background: #4a6ee0;color: white;border-top-right-radius: 4px;margin-left: auto;}.system-message {align-self: center;background: #f0f4ff;color: #666;font-style: italic;text-align: center;max-width: 90%;font-size: 14px;}.message-input {display: flex;padding: 15px;border-top: 1px solid #e0e6ff;background: #fafbff;}input {flex: 1;padding: 12px 15px;border: 1px solid #d0d9ff;border-radius: 20px;outline: none;font-size: 16px;transition: border-color 0.3s;}input:focus {border-color: #4a6ee0;box-shadow: 0 0 0 2px rgba(74, 110, 224, 0.2);}button {background: #4a6ee0;color: white;border: none;border-radius: 20px;padding: 12px 20px;margin-left: 10px;cursor: pointer;font-weight: bold;transition: all 0.3s;}button:hover {background: #3a5ec0;transform: translateY(-2px);}button:active {transform: translateY(0);}button:disabled {background: #cccccc;cursor: not-allowed;transform: none;}.reconnect-button {background: #ff9800;}.reconnect-button:hover {background: #f57c00;}@keyframes pulse {0% { opacity: 1; }50% { opacity: 0.5; }100% { opacity: 1; }}.connection-stats {display: flex;padding: 10px 20px;background: #f8f9ff;border-top: 1px solid #e0e6ff;font-size: 13px;color: #666;justify-content: space-between;}</style>
</head>
<body><div class="container"><header><h1>WebSocket聊天室</h1><div class="subtitle">带自动重连和连接状态监控</div></header><div class="status-bar"><div class="status disconnected" id="status"><div class="status-indicator"></div><span id="statusText">连接已断开</span><span class="reconnect-info" id="reconnectInfo"></span></div><button id="connectButton" class="reconnect-button">连接</button></div><div class="chat-container" id="chatContainer"><div class="message system-message">欢迎使用WebSocket聊天室!点击"连接"按钮开始。</div></div><div class="message-input"><input type="text" id="messageInput" placeholder="输入消息..." disabled><button id="sendButton" disabled>发送</button></div><div class="connection-stats"><span id="connectionStats">连接次数: 0 | 消息数: 0</span><span id="latencyInfo">延迟: - ms</span></div></div><script>document.addEventListener('DOMContentLoaded', function() {const statusElement = document.getElementById('status');const statusText = document.getElementById('statusText');const reconnectInfo = document.getElementById('reconnectInfo');const connectButton = document.getElementById('connectButton');const chatContainer = document.getElementById('chatContainer');const messageInput = document.getElementById('messageInput');const sendButton = document.getElementById('sendButton');const connectionStats = document.getElementById('connectionStats');const latencyInfo = document.getElementById('latencyInfo');// WebSocket和状态变量let websocket = null;let reconnectAttempts = 0;const maxReconnectAttempts = 10;let reconnectTimer = null;let connectionCount = 0;let messageCount = 0;let heartbeatInterval = null;let lastHeartbeat = null;// 使用公共的WebSocket测试服务器const serverUrl = 'wss://echo.websocket.org';// 更新连接状态UIfunction updateConnectionStatus(state, message = '') {statusElement.className = 'status ' + state;switch(state) {case 'connected':statusText.textContent = '已连接';connectButton.textContent = '断开';connectButton.classList.remove('reconnect-button');messageInput.disabled = false;sendButton.disabled = false;reconnectInfo.textContent = '';break;case 'connecting':statusText.textContent = '连接中...';connectButton.textContent = '取消';connectButton.classList.add('reconnect-button');messageInput.disabled = true;sendButton.disabled = true;break;case 'disconnected':statusText.textContent = message || '连接已断开';connectButton.textContent = '重连';connectButton.classList.add('reconnect-button');messageInput.disabled = true;sendButton.disabled = true;break;}updateStats();}// 更新统计信息function updateStats() {connectionStats.textContent = `连接次数: ${connectionCount} | 消息数: ${messageCount}`;}// 添加消息到聊天窗口function addMessage(content, type = 'received') {const messageElement = document.createElement('div');messageElement.className = `message ${type}`;messageElement.textContent = content;chatContainer.appendChild(messageElement);// 滚动到底部chatContainer.scrollTop = chatContainer.scrollHeight;if (type === 'sent' || type === 'received') {messageCount++;updateStats();}}// 计算下一次重连的延迟(指数退避)function getReconnectDelay() {const baseDelay = 1000; // 1秒基础延迟const maxDelay = 30000; // 30秒最大延迟const delay = Math.min(maxDelay, baseDelay * Math.pow(1.5, reconnectAttempts));// 添加随机性避免所有客户端同时重连return delay + Math.random() * 1000;}// 建立WebSocket连接function connect() {// 清除现有的重连计时器if (reconnectTimer) {clearTimeout(reconnectTimer);reconnectTimer = null;}// 如果已有连接,先关闭if (websocket && websocket.readyState === WebSocket.OPEN) {disconnect();return;}updateConnectionStatus('connecting');try {websocket = new WebSocket(serverUrl);websocket.onopen = function(event) {connectionCount++;reconnectAttempts = 0;updateConnectionStatus('connected');addMessage('已成功连接到服务器', 'system-message');// 启动心跳检测startHeartbeat();};websocket.onmessage = function(event) {// 检查是否是心跳响应if (event.data === 'pong') {const now = new Date().getTime();const latency = now - lastHeartbeat;latencyInfo.textContent = `延迟: ${latency} ms`;return;}addMessage(event.data);};websocket.onclose = function(event) {// 清除心跳检测stopHeartbeat();addMessage(`连接关闭: ${event.code} ${event.reason || '无原因'}`, 'system-message');// 如果不是手动断开,尝试重连if (reconnectAttempts < maxReconnectAttempts) {reconnectAttempts++;const delay = getReconnectDelay();reconnectInfo.textContent = `尝试 ${reconnectAttempts}/${maxReconnectAttempts}, 下次尝试: ${Math.round(delay/1000)}`;reconnectTimer = setTimeout(connect, delay);updateConnectionStatus('disconnected', '连接断开,正在尝试重连...');} else {updateConnectionStatus('disconnected', '连接断开,已达最大重试次数');reconnectInfo.textContent = '';}};websocket.onerror = function(error) {addMessage('连接发生错误', 'system-message');console.error('WebSocket错误:', error);};} catch (error) {console.error('无法建立连接:', error);addMessage('无法连接到服务器: ' + error.message, 'system-message');updateConnectionStatus('disconnected');}}// 关闭WebSocket连接function disconnect() {// 清除重连计时器if (reconnectTimer) {clearTimeout(reconnectTimer);reconnectTimer = null;}// 清除心跳检测stopHeartbeat();if (websocket) {websocket.close();websocket = null;}updateConnectionStatus('disconnected', '连接已断开');reconnectAttempts = 0;reconnectInfo.textContent = '';}// 发送消息function sendMessage() {const message = messageInput.value.trim();if (message && websocket && websocket.readyState === WebSocket.OPEN) {websocket.send(message);addMessage(message, 'sent');messageInput.value = '';}}// 启动心跳检测function startHeartbeat() {// 每10秒发送一次心跳heartbeatInterval = setInterval(() => {if (websocket && websocket.readyState === WebSocket.OPEN) {lastHeartbeat = new Date().getTime();websocket.send('ping');}}, 10000);}// 停止心跳检测function stopHeartbeat() {if (heartbeatInterval) {clearInterval(heartbeatInterval);heartbeatInterval = null;}}// 连接/断开按钮点击事件connectButton.addEventListener('click', function() {if (websocket && websocket.readyState === WebSocket.OPEN) {disconnect();} else if (reconnectTimer) {// 取消重连clearTimeout(reconnectTimer);reconnectTimer = null;reconnectAttempts = maxReconnectAttempts; // 阻止自动重连updateConnectionStatus('disconnected', '连接已取消');reconnectInfo.textContent = '';} else {connect();}});// 发送按钮点击事件sendButton.addEventListener('click', sendMessage);// 按Enter键发送消息messageInput.addEventListener('keypress', function(event) {if (event.key === 'Enter') {sendMessage();}});// 初始状态updateConnectionStatus('disconnected');});</script>
</body>
</html>

功能说明

这个实现包含以下核心功能:

  1. 连接状态监控

    • 直观的视觉指示器(绿色=已连接,橙色=连接中,红色=已断开)
    • 详细的连接状态文本信息
  2. 自动重连机制

    • 使用指数退避算法,重试间隔逐渐增加
    • 最大重试次数限制(10次)
    • 显示下一次重连的剩余时间
  3. 心跳检测

    • 每10秒发送一次ping消息
    • 计算并显示网络延迟
  4. 连接统计

    • 显示连接次数和消息数量
    • 实时延迟显示
  5. 用户控制

    • 可以手动连接/断开
    • 可以取消自动重连过程

使用说明

  1. 页面加载后,点击"连接"按钮建立WebSocket连接
  2. 连接成功后,可以在输入框中输入消息并发送
  3. 如果连接断开,系统会自动尝试重连
  4. 可以随时点击"断开"按钮结束连接,或点击"重连"按钮手动重新连接

这个实现提供了健壮的WebSocket连接管理,适合在生产环境中使用。

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

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

相关文章

【Vue2手录05】响应式原理与双向绑定 v-model

一、Vue2响应式原理&#xff08;底层基础&#xff09; Vue2的“响应式”核心是数据变化自动触发视图更新&#xff0c;其实现依赖Object.defineProperty API&#xff0c;但受JavaScript语言机制限制&#xff0c;存在“数组/对象修改盲区”&#xff0c;这是理解后续内容的关键。 …

探索大语言模型(LLM):Ollama快速安装部署及使用(含Linux环境下离线安装)

前言 Ollama 是一个开源的本地化大模型运行平台&#xff0c;支持用户直接在个人计算机上部署、管理和交互大型语言模型&#xff08;LLMs&#xff09;&#xff0c;无需依赖云端服务。而且其混合推理的特性也使得CPU和GPU的算力能够充分被使用&#xff0c;能够在同等配置下跑更大…

渗透测试信息收集详解

我们来详细解析一下渗透测试中信息收集&#xff08;Information Gathering&#xff09;的完整内容、步骤及工具方法。信息收集是整个渗透测试的基石&#xff0c;其深度和广度直接决定了后续测试的成功率&#xff0c;因此有“渗透测试成功与否&#xff0c;90%取决于信息收集”的…

Kafka面试精讲 Day 16:生产者性能优化策略

【Kafka面试精讲 Day 16】生产者性能优化策略 在“Kafka面试精讲”系列的第16天&#xff0c;我们将聚焦于生产者性能优化策略。这是Kafka中极为关键的技术点&#xff0c;也是大厂面试中的高频考点——尤其是在涉及高并发数据写入、日志采集、实时数仓等场景时&#xff0c;面试…

深入解析AI温度参数:控制文本生成的随机性与创造性

引言 在人工智能飞速发展的今天&#xff0c;文本生成模型如GPT系列已经成为内容创作、代码编写、对话系统等领域的核心工具。然而&#xff0c;许多用户在使用这些模型时&#xff0c;可能会发现输出结果有时过于保守和重复&#xff0c;有时又过于天马行空而缺乏连贯性。这背后其…

20250912在荣品RD-RK3588-MID开发板的Android13系统下在接电脑的时候禁止充电

20250912在荣品RD-RK3588-MID开发板的Android13系统下在接电脑的时候禁止充电 2025/9/12 10:21缘起&#xff1a;某人的电脑接荣品RD-RK3588-MID开发板的时候做APK开发板的时候&#xff0c;通过Android Studio连接荣品RD-RK3588-MID开发板。 经常断联/时断时续。投诉了/抱怨了好…

Unity Addressable System 本地服务器功能验证

1.从Package Manger里安装Addressable 注意这里有Addressables和Addressables两个包&#xff0c;前者是核心框架&#xff0c;处理跨平台通用逻辑&#xff0c;比如用 地址&#xff08;Address&#xff09;来异步加载、卸载资源&#xff1b;自动做引用计数&#xff0c;避免资源泄…

碎片化采购是座金矿:数字化正重构电子元器件分销的价值链

在电子元器件的分销江湖里&#xff0c;长期存在着一条隐秘的“鄙视链”&#xff1a;订单金额大、需求稳定的头部客户是众星捧月的“香饽饽”&#xff0c;而需求碎片化、品类繁多的小微企业长尾订单&#xff0c;则常被视作食之无味、弃之可惜的“鸡肋”。行业固有认知告诉我们&a…

Typescript - 通俗易懂的 interface 接口,创建接口 / 基础使用 / 可选属性 / 只读属性 / 任意属性(详细教程)

前言 在面向对象语言中&#xff0c;接口是一个很重要的概念&#xff0c;它是对行为的抽象&#xff0c;而具体如何行动需要由类去实现。 TypeScript 中的接口是一个非常灵活的概念&#xff0c;除了可用于 对类的一部分行为进行抽象 以外&#xff0c;也常用于对「对象的形状&…

【硬件-笔试面试题-92】硬件/电子工程师,笔试面试题(知识点:米勒效应,米勒平台)

题目汇总版--链接&#xff1a; 【硬件-笔试面试题】硬件/电子工程师&#xff0c;笔试面试题汇总版&#xff0c;持续更新学习&#xff0c;加油&#xff01;&#xff01;&#xff01;-CSDN博客 【硬件-笔试面试题-92】硬件/电子工程师&#xff0c;笔试面试题&#xff08;知识点…

C语言深度入门系列:第十一篇 - 动态内存管理与数据结构:程序世界的高效算法大师

C语言深度入门系列&#xff1a;第十一篇 - 动态内存管理与数据结构&#xff1a;程序世界的高效算法大师 本章目标 本章将深入探讨C语言中的动态内存管理和经典数据结构实现&#xff0c;这是从基础编程迈向算法工程师的关键一步。您将掌握内存的精确控制、理解各种数据结构的本质…

Go 语言开发环境安装与 GOPROXY 镜像配置(含依赖管理与版本切换技巧)

在国内搭建 Go 开发环境的最大障碍不是“怎么装”&#xff0c;而是“下不动”。本文是我在多台 Windows / macOS / Linux 机器上踩坑后的整合笔记&#xff1a;用最稳妥的安装方式 合理的镜像配置 一套通吃的依赖/版本管理流程&#xff0c;把速度、稳定性和可维护性一次性解决…

崔传波教授:以科技与人文之光,点亮近视患者的清晰视界‌

崔传波教授&#xff1a;以科技与人文之光&#xff0c;点亮近视患者的清晰视界‌在临沂新益民眼科医院&#xff0c;有这样一位眼科医师——他不仅是近视矫正领域的专家&#xff0c;更是“金视青春之光手术”的研发倡导者。‌崔传波教授‌以其深厚的学术功底、创新的技术理念和以…

如何写过滤条件wrapper的使用

模糊查询 &#xff1a;功能是&#xff1a;查询 WORK_NUM 字段包含 ${workOrder.workNum} 的记录。<if test"workOrder.workNum ! null and workOrder.workNum ! ">and b.WORK_NUM like CONCAT(%,CONCAT(#{workOrder.workNum},%)) </if>一、比较条件方法示…

【Spring Boot 报错已解决】彻底解决 “Main method not found in class com.xxx.Application” 报错

文章目录引言一、问题描述1.1 报错示例1.2 报错分析1.3 解决思路二、解决方法2.1 方法一&#xff1a;添加标准的main方法2.2 方法二&#xff1a;检查main方法的定义是否规范2.3 方法三&#xff1a;检查主类的位置是否正确2.4 方法四&#xff1a;重新构建项目并清理缓存三、其他…

配置自签证书多域名的动态网站+部署http的repo仓库+基于nfs与yum仓库的http部署

1.配置自签证书多域名的动态网站1.1配置自签证书1.1.1配置仓库[rootapache ~]# vim /etc/yum.repos.d/epel.repo [epel] nameepel baseurlhttps://mirrors.aliyun.com/epel/9/Everything/x86_64/ gpgcheck0 1.1.2安装easy-rsa工具(用于生成和…

【开题答辩全过程】以 12306候补购票服务系统为例,包含答辩的问题和答案

个人简介一名14年经验的资深毕设内行人&#xff0c;语言擅长Java、php、微信小程序、Python、Golang、安卓Android等开发项目包括大数据、深度学习、网站、小程序、安卓、算法。平常会做一些项目定制化开发、代码讲解、答辩教学、文档编写、也懂一些降重方面的技巧。感谢大家的…

计算机毕业设计 基于深度学习的酒店评论文本情感分析研究 Python毕业设计项目 Hadoop毕业设计选题 机器学习选题【附源码+文档报告+安装调试】

博主介绍&#xff1a;✌从事软件开发10年之余&#xff0c;专注于Java技术领域、Python、大数据、人工智能及数据挖掘、小程序项目开发和Android项目开发等。CSDN、掘金、华为云、InfoQ、阿里云等平台优质作者✌ &#x1f345;文末获取源码联系&#x1f345; &#x1f447;&…

嵌入式第五十二天(GIC,协处理器,异常向量表)

一.GICGIC&#xff08;Generic Interrupt Controller&#xff0c;通用中断控制器&#xff09; 是ARM架构中管理系统中断的核心组件&#xff0c;负责接收、优先级排序、分发中断信号给处理器核心。其核心功能和关键版本如下&#xff1a;核心功能1. 中断接收与分发&#xff1a;接…

基于hiprint的票据定位打印系统开发实践

基于hiprint的票据定位打印系统开发实践 在日常的Web开发中&#xff0c;我们经常需要实现打印功能&#xff0c;特别是对于票据、标签等需要精确排版的打印需求。今天我将分享一个基于hiprint插件实现的票据定位打印系统&#xff0c;重点介绍如何实现单行打印、批量打印以及金额…