使用 Spring Boot 搭建 WebSocket 服务器实现多客户端连接

在 Web 开发中,WebSocket 为客户端和服务端之间提供了实时双向通信的能力。本篇博客介绍如何使用 Spring Boot 快速搭建一个 WebSocket 服务器,并支持多客户端的连接和消息广播。

1. WebSocket 简介

WebSocket 是 HTML5 的一种协议,提供了客户端和服务器之间的全双工通信。通过 WebSocket,客户端可以与服务器进行持续连接,不用反复建立 HTTP 请求,从而降低延迟,提升通信效率。

为什么选择 Spring Boot 实现 WebSocket?

Spring Boot 简化了 WebSocket 服务器的配置与实现,使我们可以更专注于业务逻辑开发,且配合 @ServerEndpoint 注解实现更加清晰。

2. 项目环境与依赖配置

项目环境

  • Java 版本:JDK 8+
  • Spring Boot 版本:2.x
  • WebSocket 依赖spring-boot-starter-websocket

Maven 依赖

pom.xml 文件中添加 WebSocket 的依赖:

        <dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-websocket</artifactId></dependency>

3. WebSocket 服务端代码实现

在 Spring Boot 中,我们可以使用 @ServerEndpoint 注解创建 WebSocket 服务器端。以下是一个支持多客户端连接的 WebSocket 实现。

WebSocket 服务端代码解析

创建 MultiClientWebSocket 类,并实现以下功能:

  1. 记录当前在线客户端数
  2. 支持客户端连接、断开、消息接收、群发等功能
  3. 通过 @OnOpen@OnMessage@OnClose@OnError 注解处理连接的各个生命周期

完整代码如下:

import lombok.extern.slf4j.Slf4j;
import org.springframework.context.annotation.Bean;
import org.springframework.stereotype.Component;
import org.springframework.web.socket.server.standard.ServerEndpointExporter;
import javax.websocket.*;
import javax.websocket.server.ServerEndpoint;
import java.io.IOException;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicInteger;/*** WebSocketConfig*/
@Component
@ServerEndpoint("/ws/multi")
@Slf4j
public class MultiClientWebSocket {@Beanpublic ServerEndpointExporter serverEndpointExporter() {log.info("WebSocketConfig: serverEndpointExporter init WebSocketConfig 注入完成");return new ServerEndpointExporter();}/*** 记录当前在线连接数*/private static final AtomicInteger onlineCount = new AtomicInteger(0);/*** 存放所有在线的客户端*/private static final Map<String, Session> onlineClients = new ConcurrentHashMap<>();@OnOpenpublic void onOpen(Session session) {onlineCount.incrementAndGet();onlineClients.put(session.getId(), session);log.info("有新连接加入:{},当前在线客户端数为:{}", session.getId(), onlineCount.get());}@OnMessagepublic void onMessage(String message, Session session) throws IOException {System.out.println("收到客户端消息:" + message);session.getBasicRemote().sendText("服务器收到消息:" + message);}@OnClosepublic void onClose(Session session) {onlineCount.decrementAndGet(); // 在线数减1onlineClients.remove(session.getId());log.info("有一连接关闭:{},当前在线客户端数为:{}", session.getId(), onlineCount.get());}@OnErrorpublic void onError(Throwable t) {log.error("WebSocket 连接出错{}", t.getMessage());}/*** 群发消息** @param message 消息内容*/public void sendMessage(String message) {//log.info("开始给在线的客户端{}群发消息{}",onlineClients,message);for (Map.Entry<String, Session> sessionEntry : onlineClients.entrySet()) {Session toSession = sessionEntry.getValue();log.info("服务端给客户端[{}]发送消息{}", toSession.getId(), message);toSession.getAsyncRemote().sendText(message);}}}

代码解析

  • ServerEndpointExporter:Spring Boot 的 WebSocket 配置类,自动注册 @ServerEndpoint 注解声明的 WebSocket。
  • 在线客户端管理:使用 ConcurrentHashMap 存储在线客户端的 Session,通过 AtomicInteger 记录当前连接数。
  • 生命周期注解
    • @OnOpen:当客户端连接时触发,增加在线人数。
    • @OnMessage:当收到消息时触发,服务端处理消息。
    • @OnClose:当连接关闭时触发,减少在线人数。
    • @OnError:当连接出错时触发,记录错误日志。
  • 群发消息sendMessage 方法遍历所有在线客户端的 Session,实现消息广播。

4. 启动与测试

启动 Spring Boot 项目后,WebSocket 服务端地址为:ws://localhost:8080/ws/multi。可以使用 WebSocket 测试工具(例如 Apifox或浏览器控制台)测试 WebSocket 通信。

测试步骤

  1. 连接 WebSocket:客户端连接至 ws://localhost:8080/ws/multi
  2. 发送消息:客户端发送消息,服务端接收到消息后回复。
  3. 断开连接:客户端断开连接,服务端记录连接关闭信息。
import cn.hutool.json.JSONUtil;
import com.google.common.collect.Lists;
import com.lps.config.MultiClientWebSocket;
import com.lps.domain.R;
import lombok.RequiredArgsConstructor;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;import java.util.List;/*** @ClassName: WebSocketControl* @Description:* @Author: liu* @Date: 2024-10-30* @Version: 1.0**/
@RestController
@RequestMapping("/webSocket")
@RequiredArgsConstructor
public class WebSocketControl {private final MultiClientWebSocket multiClientWebSocket;@GetMapping("/sendList")public R<String> sad() {//Guava依赖List<String> strList = Lists.newArrayList("发送", "WebSocket消息","测试");String jsonStr = JSONUtil.toJsonStr(strList);multiClientWebSocket.sendMessage(jsonStr);return R.ok(jsonStr);}
}

前端测试demo

<!DOCTYPE html>
<html>
<head><meta charset="UTF-8"><title>WebSocket Demo</title><style>#status {margin-bottom: 10px;}</style><script>let ws;function connectWebSocket() {// 检查 WebSocket 是否已连接if (ws && ws.readyState === WebSocket.OPEN) {console.log('WebSocket 已连接,无需重复连接。');return;}ws = new WebSocket('ws://localhost:8080/ws/multi');ws.onopen = function() {console.log('WebSocket 连接已经建立。');document.getElementById('status').innerText = 'WebSocket 连接已建立。';ws.send('Hello, server!');};ws.onmessage = function(event) {console.log('收到服务器消息:', event.data);document.getElementById('messages').innerText += '收到消息:' + event.data + '\n';};ws.onerror = function(event) {console.error('WebSocket 连接出现错误:', event);document.getElementById('status').innerText = 'WebSocket 连接出现错误。';};ws.onclose = function() {console.log('WebSocket 连接已经关闭。');document.getElementById('status').innerText = 'WebSocket 连接已关闭。';ws = null; // 连接关闭后将 ws 设置为 null,以便重新连接};}function disconnectWebSocket() {if (ws) {ws.close();}}</script>
</head>
<body><h1>WebSocket Demo</h1><div id="status">WebSocket 连接状态</div><button onclick="connectWebSocket()">连接 WebSocket</button><button onclick="disconnectWebSocket()">断开 WebSocket</button><pre id="messages"></pre>
</body>
</html>

5. 总结

通过以上代码示例,我们可以实现一个简单的 WebSocket 服务端,支持多客户端连接和消息广播。此 WebSocket 服务端适用于需要实时消息推送的应用场景,比如聊天室、实时通知系统等。

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

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

相关文章

C# 日志框架 NLog、log4net 和 Serilog对比

文章目录 前言NLog、log4net 和 Serilog 三个框架的详细对比:一、NLog优点:缺点:二、 log4net优点缺点三、Serilog优点缺点四、Serilog使用举例总结前言 NLog、log4net 和 Serilog 三个框架的详细对比: NLog、log4net 和 Serilog 是三个非常流行的 .NET 日志框架,它们各自…

从0开始本地部署大模型

这就开始从0开始本地部署大模型 下载Ollama 下载地址&#xff1a;https://ollama.com/download/windows 适用于MacOS、Linux和Windows&#xff0c;这里我下载Windows的安装包。 直接打开安装包&#xff0c;点击install即可&#xff0c;安装完成后可以在任务栏中看到Ollama程…

RHCSA课后练习3(网络与磁盘)

1、配置网络&#xff1a;为网卡添加一个本网段IPV4地址&#xff0c;x.x.x.123 涉及的知识点 配置网络&#xff1a; ens160&#xff1a;en---表示以太网 wl---表示无线局域网 ww---表示无线广域网 注意&#xff1a;一个网络接口&#xff0c;可以有多个网络连接&#xff0c;但…

Linux:网络协议socket

我们之前学的通信是本地进程间通信&#xff0c;如果我们想在网络间通信的话&#xff0c;就需要用到二者的ip地址&#xff0c;分别被称为源IP地址和目的IP地址&#xff0c;被存入ip数据包中&#xff0c;其次我们还需要遵循一些通信协议。 TCP协议&#xff1a;传输层协议&#x…

相机硬触发

PLC 接线图 通过使用PNP光电感应器 实现相机的硬触发 流程&#xff1a;触发相机拍照 然后相机控制光源触发 完成线路连接后 使用MVS 配置相机硬触发参数 通过 pnp传感器控制 硬触发拍照 检测 在2开项目中 不用在点击执行流程 通过PNP传感器就能触发 扩展&#xff1a; 在VP…

浅谈UI自动化

⭐️前言⭐️ 本篇文章围绕UI自动化来展开&#xff0c;主要内容包括什么是UI自动化&#xff0c;常用的UI自动化框架&#xff0c;UI自动化原理等。 &#x1f349;欢迎点赞 &#x1f44d; 收藏 ⭐留言评论 &#x1f349;博主将持续更新学习记录收获&#xff0c;友友们有任何问题…

儿童安全座椅行业全面深入分析

儿童安全座椅就是一种专为不同体重&#xff08;或年龄段&#xff09;的儿童设计&#xff0c;将孩子束缚在安全座椅内&#xff0c;能有效提高儿童乘车安全的座椅。欧洲强制性执行标准ECE R44/03的定义是&#xff1a;能够固定到机动车辆上&#xff0c;带有ISOFIX接口、LATCH接口的…

net core Autofac 替换默认的服务容器 DI,微软自动的容器 不支持命名选项的

微软默认的容器&#xff0c;不支持命名选项&#xff0c;同一接口&#xff0c;多个实现。 就不支持了。 配置core 支持Autofac 容器 using Autofac; using Autofac.Extensions.DependencyInjection;namespace WebApplication13 {public interface IMyService{string GetData()…

架构系列---高并发

目录标题 前言宏观架构细节解读第一层 &#xff1a;DNS第二层 &#xff1a; LVS 负载第三层 &#xff1a; Nginx第四层 &#xff1a; Gateway Application并发上限更多方案 业务扩展从域名角度如何承受更大的流量从业务的角度看如何分流大的流量 总结 前言 年轻的时候看到文章…

植被遥感常用反射特征表达

Figure: HDRF Let Ω ′ \Omega Ω′ be the incident solid angle, Ω \Omega Ω is leaving solid angle. Consider the BRDF of a Lamvertian target is 1 π \frac{1}{\pi} π1​, the BRF is 1. The HDRF of a target is defined as: R h e m ( Ω ) Φ r Φ r l a …

使用 MONAI Deploy 在 AMD GPU 上进行全身分割

Total body segmentation using MONAI Deploy on an AMD GPU — ROCm Blogs 2024 年 4 月 4 日 作者&#xff1a; Vara Lakshmi Bayanagari. 医疗开放网络人工智能&#xff08;MONAI&#xff09;是一个开源组织&#xff0c;提供最先进的医疗成像模型的 PyTorch 实现&#xff0c…

解决 ClickHouse 高可用集群中 VRID 冲突问题:基于 chproxy 和 keepalived 的实践分析

Part1背景描述 近期&#xff0c;我们部署了两套 ClickHouse 生产集群&#xff0c;分别位于同城的两个数据中心。这两套集群的数据保持一致&#xff0c;以便在一个数据中心发生故障时&#xff0c;能够迅速切换应用至另一个数据中心的 ClickHouse 实例&#xff0c;确保服务连续性…

推荐FileLink数据跨网摆渡系统 — 安全、高效的数据传输解决方案

在数字化转型的浪潮中&#xff0c;企业对于数据传输的需求日益增加&#xff0c;特别是在不同网络环境之间的文件共享和传输。为了满足这一需求&#xff0c;FileLink数据跨网摆渡系统应运而生&#xff0c;为企业提供了一种安全、高效的数据传输解决方案。 安全第一&#xff0c;保…

力扣排序350题 两个元组的交集2

题目&#xff1a; 给你两个整数数组 nums1 和 nums2 &#xff0c;请你以数组形式返回两 数组的交集。返回结果中每个元素出现的次数&#xff0c;应与元素在两个 数组中都出现的次数一致&#xff08;如果出现次数不一致&#xff0c;则考虑取 较小值&#xff09;。可以不考虑输出…

Python酷库之旅-第三方库Pandas(193)

目录 一、用法精讲 896、pandas.Index.isna方法 896-1、语法 896-2、参数 896-3、功能 896-4、返回值 896-5、说明 896-6、用法 896-6-1、数据准备 896-6-2、代码示例 896-6-3、结果输出 897、pandas.Index.notna方法 897-1、语法 897-2、参数 897-3、功能 897…

使用Mac如何才能提高OCR与翻译的效率

OCR与截图大家都不陌生&#xff0c;或许有的朋友对于这两项功能用到的不多&#xff0c;但是如果经常会用到的话&#xff0c;那你就该看看了 iOCR&#xff0c;快捷键唤出翻译窗口&#xff0c;不论是截图翻译、划词翻译、输入翻译、剪切板翻译&#xff0c;统统快捷键完成&#x…

《欢乐饭米粒儿9》第五期:用笑声诠释生活,让爱成为日常

在忙碌的生活节奏中&#xff0c;我们总是在寻找那份能够触动心灵深处的温暖与欢笑。由鲜博士独家冠名播出的独创小品剧《欢乐饭米粒儿》第九季作为一档家庭喜剧节目&#xff0c;正是这样一股清流&#xff0c;它以轻松幽默的方式&#xff0c;将家的温暖、爱的传递和生活的真谛娓…

基于人脸识别PCA算法matlab实现及详细步骤讲解

人脸识别 % FaceRec.m % PCA 人脸识别修订版&#xff0c;识别率88&#xff05; % calc xmean,sigma and its eigen decomposition allsamples[];%所有训练图像 for i1:40 for j1:5 aimread(strcat(e:\ORL\s,num2str(i),\,num2str(j),.jpg)); % imshow(a); ba(1:112*92…

C#的Event事件示例小白级剖析

1、委托Delegate 首先说一下delegate委托&#xff0c;委托是将方法作为参数进行传递。 // 定义了一个委托类型public delegate void MyDelegate(int num);// 定义了一个啥也不干的委托实例public MyDelegate m_delegate _ > {};// 定义了一个和委托相同格式的方法public …

Android 使用ninja加速编译的方法

ninja的简介 随着Android版本的更迭&#xff0c;makefile体系逐渐增多&#xff0c;导致make单编模块的时间越来越长&#xff0c;每次都需要半个小时甚至更长时间&#xff0c;其原因为每次make都会重新加载所有mk文件&#xff0c;再生成ninja编译&#xff0c;此完整过程十分耗时…