42、JavaEE高级主题:WebSocket详解
WebSocket
一、WebSocket协议与实现
WebSocket是一种基于TCP协议的全双工通信协议,能够在客户端和服务器之间建立实时、双向的通信通道。通过WebSocket,客户端和服务器可以在任何时候发送数据,并立即接收到对方的响应。
1.1 WebSocket协议的特点
- 全双工通信:客户端和服务器可以同时发送和接收消息。
- 实时性:数据的传输是实时的,服务器可以主动推送数据给客户端。
- 低延迟:相比HTTP协议,WebSocket的握手和数据传输延迟更低。
- 高效性:WebSocket的数据帧格式简单,减少了数据传输的开销。
1.2 WebSocket的握手过程
WebSocket的连接建立需要通过HTTP协议的握手过程:
- 客户端请求:客户端发送一个HTTP请求,表示希望建立WebSocket连接。请求中包含
Upgrade: websocket和Connection: Upgrade头字段。 - 服务器响应:服务器返回一个HTTP响应,包含
Upgrade: websocket和Connection: Upgrade头字段,表示同意建立WebSocket连接。 - WebSocket连接建立:握手完成后,HTTP连接升级为WebSocket连接,客户端和服务器可以开始双向通信。
1.3 数据帧格式
WebSocket的数据帧格式如下:
0 1 2 3 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3
+-+-+-+-+-+-+
|F| RSV | OPCODE | MASK | LENGTH |
+-+-+-+-+-+-+
| PAYLOAD |
+-+-+-+-+-+-+
- F: 最高位,表示是否是终端帧(FIN)。
- RSV: 保留位,目前未使用。
- OPCODE: 操作码,用于指示数据的类型。
- MASK: 表示是否对负载数据进行了掩码处理。
- LENGTH: 数据的长度。
- PAYLOAD: 实际传输的数据内容。
1.4 心跳机制
为了保持连接的活性,WebSocket可以通过心跳机制定期发送心跳包,防止连接因长时间无数据传输而被关闭。
1.5 Java中的WebSocket实现
Java提供了两种方式来实现WebSocket:
- Java API for WebSocket (JSR-356):这是一个标准的Java API,提供了
@ServerEndpoint、@OnOpen、@OnClose、@OnError和@OnMessage等注解,用于快速开发WebSocket应用。 - 第三方框架(如Spring WebSocket):Spring提供了更高级别的抽象,使得WebSocket的开发更加简单和灵活。
二、实时通信场景
WebSocket在以下场景中有广泛的应用:
2.1 实时聊天室
- 点对点聊天:两个用户之间进行实时消息交流。
- 群聊:多个用户加入同一个聊天室,消息可以广播给所有用户。
- 消息记录:记录聊天历史,方便用户回顾。
2.2 在线监控系统
- 实时数据推送:例如,服务器性能监控、物联网设备状态监控等。
- 动态更新:客户端界面可以实时更新,例如股票价格、天气预报等。
2.3 其他场景
- 在线游戏:支持玩家之间的实时交互。
- 实时协作:例如,多人同时编辑文档或表格。
- 通知系统:实时推送用户通知,例如邮件、消息提醒等。
三、实践1:实现实时消息功能
以下是一个基于Java API for WebSocket的简单聊天室示例:
3.1 服务端实现
import javax.websocket.OnClose;
import javax.websocket.OnError;
import javax.websocket.OnMessage;
import javax.websocket.OnOpen;
import javax.websocket.server.ServerEndpoint;
import java.io.IOException;
import java.util.concurrent.CopyOnWriteArrayList; @ServerEndpoint("/chat")
public class ChatServer { private static CopyOnWriteArrayList<ChatServer> clients = new CopyOnWriteArrayList<>(); @OnOpen public void onOpen() { clients.add(this); System.out.println("新客户端连接"); } @OnClose public void onClose() { clients.remove(this); System.out.println("客户端断开"); } @OnError public void onError(Throwable throwable) { System.out.println("发生错误:" + throwable.getMessage()); } @OnMessage public void onMessage(String message) { System.out.println("收到消息:" + message); broadcast(message); } private void broadcast(String message) { for (ChatServer client : clients) { try { client.sendMessage(message); } catch (IOException e) { e.printStackTrace(); } } } private void sendMessage(String message) throws IOException { // 向客户端发送消息 // 具体实现取决于底层的WebSocket库或框架 }
}
3.2 客户端实现
const websocket = new WebSocket('ws://localhost:8080/chat'); websocket.onopen = function(event) { console.log('连接到服务器'); websocket.send('客户端连接成功!');
}; websocket.onmessage = function(event) { console.log('收到消息:' + event.data); document.getElementById('chat-log').innerHTML += '<br>' + event.data;
}; websocket.onclose = function(event) { console.log('连接关闭');
}; websocket.onerror = function(event) { console.log('发生错误');
}; // 发送消息
function sendMessage() { const messageInput = document.getElementById('message'); const message = messageInput.value; websocket.send(message); messageInput.value = '';
}
四、实践2:在线实时监控系统
在上一部分中,我们实现了一个简单的实时聊天室功能。接下来,我们将探讨一个更复杂的案例:在线实时监控系统。在线监控系统是一种需要实时数据推送的典型场景,能够展示WebSocket在实际应用中的强大功能。
4.1 项目背景
假设我们需要开发一个实时监控系统,用于监控多台服务器的运行状态,包括:
- CPU使用率
- 内存使用率
- 磁盘使用率
- 网络带宽
- 系统负载
此外,该系统需要支持以下功能:
- 实时数据推送:服务器状态数据实时更新。
- 历史数据查询:用户可以查看任意时间段内的服务器状态数据。
- 异常告警:当服务器状态超过阈值时,触发告警。
- 多客户端支持:支持多个客户端同时连接,并接收实时数据。
4.2 项目结构
我们将使用以下技术栈来实现这个系统:
- 后端:使用
Spring Boot框架,集成WebSocket来实现实时通信。(springboot框架没学过的看我的后续相关博文) - 前端:使用
Vue.js来构建用户界面。 - 数据库:使用
MySQL存储历史数据。 - 数据模拟:使用随机数生成模拟服务器状态数据(可替换为真实数据源)。
4.3 实现步骤
1. 项目初始化
创建一个Spring Boot项目,并引入以下依赖:
<dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-websocket</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-jpa</artifactId> </dependency> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> </dependency>
</dependencies>
2. 数据模型
创建实体类ServerStatus,用于存储服务器状态数据:
@Entity
public class ServerStatus { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id; private String serverName; private Double cpuUsage; private Double memoryUsage; private Double diskUsage; private Double networkBandwidth; private Date timestamp; // Getters and Setters
}
3. WebSocket服务端实现
创建WebSocket服务端,负责数据采集、推送和告警:
@Component
@ServerEndpoint("/monitor")
public class ServerMonitor { private static final List<ServerMonitor> clients = new CopyOnWriteArrayList<>(); @Autowired private ServerStatusService serverStatusService; @OnOpen public void onOpen() { clients.add(this); System.out.println("客户端连接成功"); } @OnClose public void onClose() { clients.remove(this); System.out.println("客户端断开连接"); } @OnError public void onError(Throwable throwable) { System.out.println("发生错误:" + throwable.getMessage()); } @OnMessage public void onMessage(String message) { System.out.println("收到消息:" + message); // 处理客户端发送的消息(例如,客户端请求历史数据) if (message.startsWith("query_history")) { String serverName = message.split("_")[1]; List<ServerStatus> history = serverStatusService.getHistoryByServerName(serverName); sendHistoryData(history); } } public void broadcastStatus(ServerStatus status) { String json = JSON.toJSONString(status); for (ServerMonitor client : clients) { try { client.sendMessage(json); } catch (IOException e) { e.printStackTrace(); } } } public void sendMessage(String message) throws IOException { // 向客户端发送消息 // 消息格式:JSON格式的服务器状态数据 synchronized (this.session) { this.session.getBasicRemote().sendText(message); } } public void sendHistoryData(List<ServerStatus> history) { String json = JSON.toJSONString(history); for (ServerMonitor client : clients) { try { client.sendHistoryMessage(json); } catch (IOException e) { e.printStackTrace(); } } } private void sendHistoryMessage(String json) throws IOException { synchronized (this.session) { this.session.getBasicRemote().sendText("history_" + json); } }
}
4. 数据采集和推送
创建一个定时任务,模拟服务器状态数据并推送:
@Component
public class ServerDataCollector { @Autowired private ServerStatusService serverStatusService; @Scheduled(fixedRate = 1000) public void collectAndPushData() { // 模拟服务器状态数据 List<ServerStatus> statuses = new ArrayList<>(); statuses.add(createServerStatus("Server-01")); statuses.add(createServerStatus("Server-02")); for (ServerStatus status : statuses) { serverStatusService.saveStatus(status); } // 推送实时数据 for (ServerStatus status : statuses) { new ServerMonitor().broadcastStatus(status); } } private ServerStatus createServerStatus(String serverName) { ServerStatus status = new ServerStatus(); status.setServerName(serverName); status.setCpuUsage(Math.random() * 100); status.setMemoryUsage(Math.random() * 100); status.setDiskUsage(Math.random() * 100); status.setNetworkBandwidth(Math.random() * 100); status.setTimestamp(new Date()); return status; }
}
5. 异常检测和告警
在数据采集后,添加异常检测逻辑:
@Service
public class ServerStatusService { @Autowired private ServerStatusRepository repository; public void saveStatus(ServerStatus status) { repository.save(status); checkThreshold(status); } public List<ServerStatus> getHistoryByServerName(String serverName) { return repository.findByServerName(serverName); } private void checkThreshold(ServerStatus status) { if (status.getCpuUsage() > 80) { triggerAlarm("CPU使用率超过80%》,当前:" + status.getCpuUsage()); } if (status.getMemoryUsage() > 85) { triggerAlarm("内存使用率超过85%》,当前:" + status.getMemoryUsage()); } } private void triggerAlarm(String message) { // 发送告警消息给客户端 new ServerMonitor().broadcastStatus(new ServerStatus()); // 其他处理逻辑,例如发送邮件、短信等 }
}
6. 前端实现
使用Vue.js创建实时监控界面:
<template> <div class="monitor-container"> <div class="server-status" v-for="status in statuses" :key="status.serverName"> <h2>{{ status.serverName }}</h2> <div class="metric-container"> <div class="metric"> <label>CPU使用率</label> <div class="progress-bar"> <div :style="{width: status.cpuUsage + '%'}"></div> </div> <span>{{ status.cpuUsage.toFixed(2) }}%</span> </div> <!-- 其他指标 --> </div> </div> </div>
</template> <script>
export default { data() { return { ws: null, statuses: [] } }, mounted() { this.ws = new WebSocket('ws://localhost:8080/monitor'); this.ws.onmessage = (event) => { if (event.data.startsWith("history_")) { this.historyData = JSON.parse(event.data.split("history_")[1]); } else { this.statuses = JSON.parse(event.data); } }; }
}
</script>
4.4 功能说明
- 实时数据推送:后端定时生成模拟数据并通过WebSocket推送到客户端,客户端实时更新界面。
- 历史数据查询:客户端可以通过发送特定的消息(如
query_history_Server-01)来查询任意时间段的历史数据。 - 异常告警:当服务器状态超过阈值时,后端触发告警,并通过WebSocket通知客户端。
- 多客户端支持:WebSocket协议支持多个客户端同时连接,并接收实时数据。
4.5 总结
通过这个复杂的案例,我们展示了如何利用WebSocket协议实现实时监控系统。该系统不仅支持实时数据推送,还结合了历史数据查询、异常告警等功能,体现了WebSocket在实时通信中的强大能力。
这种架构可以扩展到更多复杂场景,例如:
- 集成更多监控项(如网络延迟、请求响应时间等)
- 支持多种数据可视化方式(如曲线图、柱状图等)
- 提供更完善的安全认证机制
- 支持多种终端(如移动端、桌面端等)
五、总结
WebSocket是一种强大的协议,能够实现客户端和服务器之间的实时、双向通信。它在实时聊天、在线监控、游戏开发等场景中有广泛的应用。本节通过理论和实践相结合的方式,介绍了WebSocket的核心概念、实现方式以及实际应用场景。通过学习本节内容,可以掌握WebSocket的基本使用方法,并能够在实际项目中应用它。
相关文章:
42、JavaEE高级主题:WebSocket详解
WebSocket 一、WebSocket协议与实现 WebSocket是一种基于TCP协议的全双工通信协议,能够在客户端和服务器之间建立实时、双向的通信通道。通过WebSocket,客户端和服务器可以在任何时候发送数据,并立即接收到对方的响应。 1.1 WebSocket协议…...
Http代理服务器选型与搭建
代理服务器选型-Squid 缓存加速 缓存频繁访问的网页、图片等静态资源,减少对原始服务器的重复请求,提升响应速度支持HTTP、HTTPS、FTP等协议,通过本地缓存直接响应客户端请求 访问控制 基于ACL(访问控制列表)实现精细…...
蓝桥杯第十一届省赛C++B组真题解析
蓝桥杯第十一届省赛CB组真题解析 八、回文日期https://www.lanqiao.cn/problems/348/learning 方法一:暴力枚举所有的日期,记录有多少个回文日期。 #include <bits/stdc.h> using namespace std; int month[13]{0,31,28,31,30,31,30,31,31,30,31…...
Linux主要开发工具之gcc、gdb与make
此系列还有两篇,大家想完整掌握可以阅读另外两篇 Linux文本编辑与shell程序设计-CSDN博客 Linux基础知识详解与命令大全(超详细)-CSDN博客 1.gcc编译系统 1.1 文件名后缀 文件名后缀 文 件 类 型 文件名后缀 文 件 类 型 .c C源…...
排序算法(快速排序,选择排序......)【泪光2929】
hello,大家好!今天给大家分享一下各种排序: 1,选择排序 首先从原始数组中 选择最小的1个数据,将其和位于第1个位置的数据交换。接着从剩下的n-1个数据中选择次小的1个元素,将其和第2个位置的数据交换然后…...
Conda使用方法详解
Conda是一个开源的包管理和环境管理系统,主要用于Python/R等科学计算领域,可以轻松管理不同项目的依赖关系。以下是Conda的详细使用方法: 一、安装与配置 1.安装Miniconda/Anaconda Miniconda是精简版,只包含conda和Python Ana…...
数据库的MVCC机制详解
MVCC(Multi-Version Concurrency Control,多版本并发控制)是数据库系统中常用的并发控制机制,它允许数据库在同一时间点保存数据的多个版本,从而实现非阻塞的读操作,提高并发性能。 MVCC的核心思想是&…...
C++初阶-C++入门基础
目录 编辑 1.C的简介 1.1C的产生和发展 1.2C的参考文档 1.3C优势和难度 1.4C学习的建议 2.C的第一个程序 2.1打印Hello world 2.2头文件 2.3namespace命名空间 2.4::作用域限定符 2.5namespace的延伸 2.6C的输入输出 3.总结 1.C的简介 …...
关于量化交易在拉盘砸盘方面应用的部分思考
关于“砸盘”的深层解析与操盘逻辑 一、砸盘的本质与市场含义 砸盘指通过集中抛售大量筹码导致价格快速下跌的行为,其核心目标是制造恐慌、清洗浮筹或实现利益再分配。不同场景下的砸盘含义不同: 主动砸盘(操控…...
idea手动创建resources文件夹
有时maven没有构建成功可能造成,resources文件夹不创建的现象 此时我们可以手动创建 手动创建...
第十五届蓝桥杯大赛软件赛省赛Python 大学 C 组题目试做(中)【本期题目:回文数组,挖矿】
OK,继续写我们的第十五届蓝桥杯大赛软件赛省赛Python 大学 C 组题目,后面的题目比较麻烦了,所以我们再分两期讲。 这一期的题有 : 回文数组,挖矿 文章目录 回文数组基本思路第一步,获取半个数组每个数需要…...
Qt动画 QAbstractAnimation
文章目录 简介QVariantAnimation 数值动画QPropertyAnimation 属性动画 QAnimationGroup 一组动画QParallelAnimationGroup 并行动画组QSequentialAnimationGroup 串行动画组 简介 QAbstractAnimation 是所有 Qt 动画的基类。 该类定义了所有动画应该都会有的功能函数。 要想实…...
SpringMvc的请求-获得请求参数
客户端请求参数的格式是: namevalue&namevalue..… 服务器端要获得请求的参数,有时还需要进行数据的封装,SpringMVC可以接收如下类型的参数: 基本类型参数 POJO类型参数 数组类型参数 集合类型参数 获得基本类型参数 Controller中的业务方法…...
flutter开发音乐APP(前提准备)
1、项目的一些环境: 2、接口文档: 酷狗音乐 NodeJS 版 API 3、接口数据结构化 Instantly parse JSON in any language | quicktype UI样式借鉴参考: Coffee-Expert/Apple-Music-New-UI: Apple Music Clone on Flutter, with redesigned UI…...
使用docker搭建redis镜像时云服务器无法访问到国外的docker官网时如何解决
下载redis镜像 docker redis:版本号 此时截图中无法访问到国外的docker官网 解决方案: 通过更换镜像源来正常下载redis镜像 sudo mkdir -p /etc/docker sudo tee /etc/docker/daemon.json <<EOF {"registry-mirrors": ["https://docker.1…...
双引擎驱动:解密音视频体验的QoS技术底座与QoE感官革命
QoS 定义:QoS(Quality of Service,服务质量)衡量音视频传输技术层面的性能表现,聚焦网络传输和系统处理能力,通过客观指标量化服务质量。核心指标 码率/带宽:数据传输速率上限,直接…...
Qt之QNetworkInterface
简介 用于表示网络接口(即网卡)信息 常用接口 static QList<QNetworkInterface> allInterfaces(); static QList<QHostAddress> allAddresses(); QList<QNetworkAddressEntry> addressEntries() const;接口类型 用枚举InterfaceTy…...
pom导包成功,但是就是无法使用相关类,同时报错:Library:Maven ‘xxx‘ has broken path
开发环境:Intellij 2023 一、问题记录 在maven工程的pom文件导入如下某一依赖(JGit)。没有显示导包的错误,同时在maven仓库里面找到对应的包是正常下载到相应jar的。 但是就是无法引入相关的类。打开Project Structure,在Dependencies中发现…...
大数据技术之Scala
Spark运行架构核心是一个计算引擎 核心组件 1. Driver(驱动器) 角色:Spark作业的“大脑”,负责解析用户代码、生成任务并调度执行。 功能: 将用户程序转换为作业(Job)。 …...
LeetCode刷题常见的Java排序
1. 字符串排序(字母排序) 首先,你的代码实现了根据字母表顺序对字符串中的字母进行排序,忽略了大小写并且保留了非字母字符的位置。关键点是: 提取和排序字母:通过 Character.isLetter() 判断是否为字母,并利用 Character.toLowerCase() 来忽略大小写进行排序。保留非字…...
mysql的下载和安装2025.4.8
mysql下载和安装 MySQL的下载网址: https://www.mysql.com/downloads/ 点击进入Windows版本下载:我们可以选择需要的MySQL版本以及所需的操作系统,这里选择离线安装: 注意:MySQL 8.0 是带有 MySQL Installer 的最后一…...
QML Loader:延迟加载与动态切换
目录 引言相关阅读工程结构LoaderDelay.qml - 延迟加载实现完整代码HeavyComponent.qml代码解析运行效果 LoaderSwitch.qml - 动态切换组件完整代码代码解析运行效果 Main.qml - 主界面实现完整代码主界面结构代码解析 总结下载链接 引言 QML的Loader组件提供了一种强大的机制…...
Python和MicroPython的解释器区别
Python和MicroPython的解释器不是同一个,它们在设计目标、实现方式和运行环境上都有显著的区别。以下是它们的主要区别: 1. 底层实现 Python解释器(CPython): Python的标准解释器是CPython(C语言实现的Pyt…...
Git 的进阶功能和技巧
1、分支的概念和使用 1.1、什么是分支? 分支(Branch)是在版本控制中非常重要的概念。几乎所有版本控制系统都支持某种形式的分支。在 Git 中,分支是 Git 强大功能之一,它允许我们从主开发线分离出来,在不…...
解析HiveQL的ALTER TABLE ADD/REPLACE COLUMNS语句
阅读以下ALTER TABLE的ADD/REPLACE COLUMNS语句的语法,用C#编写解析函数,一个一个字符解析,所有关键字不区分大小写,一个或多个空格、Tab和换行的组合都可以是关键词之间的分隔,表名和字段名可能包含空格和Tab,语句中可以用`包裹表名和字段名,解析以下HiveQL语句在所有可…...
Spark Core编程
一 Spark 运行架构 1 运行架构 定义 Spark 框架的核心是一个计算引擎,整体来说,它采用了标准 master-slave 的结构 如图所示 2 核心组件 Spark 框架有两个核心组件: 1)Driver 2)Spark 驱动器节点(用于执行 Spark 任务中的 main 方法&…...
在Ubuntu内网环境中为Gogs配置HTTPS访问(通过Apache反向代理使用IP地址)
一、准备工作 确保已安装Gogs并运行在HTTP模式(默认端口3000) 确认服务器内网IP地址(如192.168.1.100) 二、安装Apache和必要模块 sudo apt update sudo apt install apache2 -y sudo a2enmod ssl proxy proxy_http rewrite headers 三、创建SSL证书 1. 创建证书存储目录…...
Kafka和RocketMQ相比有什么区别?那个更好用?
Kafka和RocketMQ相比有什么区别?那个更好用? Kafka 和 RocketMQ 都是广泛使用的消息队列系统,它们有很多相似之处,但也有一些关键的区别。具体选择哪个更好用,要根据你的应用场景和需求来决定。以下是它们之间的主要区别: 1. …...
无人机装调与测试
文章目录 前言一、无人机基本常识/预备知识(一)无人机飞行原理无人机硬件组成/各组件作用1.飞控2.GPS3.接收机4.电流计5.电调6.电机7.电池8.螺旋桨9.UBEC(稳压模块) (二)飞控硬件简介(三&#x…...
JavaScript Hook JSON.stringify和JSON.parse:逆向与修改实战指南
在JavaScript逆向工程中,Hook JSON.stringify和JSON.parse方法是一种重要的技术,可以用来捕获、修改或分析JSON数据的序列化和反序列化过程。本文将结合具体案例,详细讲解如何实现这些方法的Hook操作。 一、Hook JSON.stringify和JSON.parse…...
