SpringBoot实现WebSocket

news2024/11/22 22:56:50

一、什么是websocket

WebSocket是HTML5下一种新的协议(websocket协议本质上是一个基于tcp的协议)
它实现了浏览器与服务器全双工通信,能更好的节省服务器资源和带宽并达到实时通讯的目的
Websocket是一个持久化的协议

二、新建SpringBoot工程

在这里插入图片描述
按照上图这样配置即可,点击下一步
因为本文章只实现websocket功能,所以只勾选SpringWeb,同时要保证springboot版本要低于3.0
在这里插入图片描述

创建完的工程目录如下
在这里插入图片描述

三、修改配置文件

将application.properties修改为application.yml,然后修改内容为

#服务器配置
server:
  port: 8081
  servlet:
    context-path: /api

在这里插入图片描述

修改POM.xml,在dependencies下加入所需依赖,加入后在空白处右键点击重新加载项目,下载对应依赖

<dependency>
	<groupId>org.springframework</groupId>
	<artifactId>spring-websocket</artifactId>
	<version>5.2.8.RELEASE</version>
</dependency>
<!--工具类 -->
	<dependency>
	<groupId>cn.hutool</groupId>
	<artifactId>hutool-all</artifactId>
	<version>5.4.1</version>
</dependency>

新建一个websocket包,包中新建两个类,代码如下

package com.example.demo.websocket;

import cn.hutool.json.JSONUtil;
import org.springframework.stereotype.Component;

import javax.websocket.OnClose;
import javax.websocket.OnMessage;
import javax.websocket.OnOpen;
import javax.websocket.Session;
import javax.websocket.server.PathParam;
import javax.websocket.server.ServerEndpoint;
import java.io.IOException;
import java.util.concurrent.ConcurrentHashMap;

@ServerEndpoint(value = "/websocket/{userId}")
@Component
public class WebSocket {
	private static ConcurrentHashMap<String, WebSocket> webSocketMap = new ConcurrentHashMap<>();
	//实例一个session,这个session是websocket的session
	private Session session;

	//新增一个方法用于主动向客户端发送消息
	public static void sendMessage(Object message, String userId) {
		WebSocket webSocket = webSocketMap.get(userId);
		if (webSocket != null) {
			try {
				webSocket.session.getBasicRemote().sendText(JSONUtil.toJsonStr(message));
				System.out.println("【websocket消息】发送消息成功,用户="+userId+",消息内容"+message.toString());
			} catch (IOException e) {
				e.printStackTrace();
			}
		}
	}
	
	public static ConcurrentHashMap<String, WebSocket> getWebSocketMap() {
		return webSocketMap;
	}

	public static void setWebSocketMap(ConcurrentHashMap<String, WebSocket> webSocketMap) {
		WebSocket.webSocketMap = webSocketMap;
	}

	//前端请求时一个websocket时
	@OnOpen
	public void onOpen(Session session, @PathParam("userId") String userId) {
		this.session = session;
		webSocketMap.put(userId, this);
		sendMessage("CONNECT_SUCCESS", userId);
		System.out.println("【websocket消息】有新的连接,连接id"+userId);
	}

	//前端关闭时一个websocket时
	@OnClose
	public void onClose() {
		webSocketMap.remove(this);
		System.out.println("【websocket消息】连接断开,总数:"+ webSocketMap.size());
	}

	//前端向后端发送消息
	@OnMessage
	public void onMessage(String message) {
		if (!message.equals("ping")) {
			System.out.println("【websocket消息】收到客户端发来的消息:"+message);
		}
	}
}

package com.example.demo.websocket;


import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.socket.server.standard.ServerEndpointExporter;

//开启WebSocket的支持,并把该类注入到spring容器中
@Configuration
public class WebSocketConfig {

	@Bean
	public ServerEndpointExporter serverEndpointExporter() {
		return new ServerEndpointExporter();
	}

}

四、项目完整结构如下

在这里插入图片描述

五、测试功能

点击运行按钮,可以看到控制台对应的输出,输出看到JVM running for 表示启动成功
在这里插入图片描述

在桌面新建一个websocket.html,将代码复制进去

<!DOCTYPE html>
<html lang="en">
	<head>
		<meta charset="utf-8" />
		<meta http-equiv="X-UA-Compatible" content="IE=edge">
		<meta name="viewport" content="width=device-width, initial-scale=1">
		<title>本地websocket测试</title>
		<meta name="robots" content="all" />
		<meta name="keywords" content="本地,websocket,测试工具" />
		<meta name="description" content="本地,websocket,测试工具" />
		<style>
			.btn-group{
				display: inline-block;
			}
		</style>
	</head>
	<body>
		<input type='text' value='通信地址, ws://开头..' class="form-control" style='width:390px;display:inline'
		 id='wsaddr' />
		<div class="btn-group" >
			<button type="button" class="btn btn-default" onclick='addsocket();'>连接</button>
			<button type="button" class="btn btn-default" onclick='closesocket();'>断开</button>
			<button type="button" class="btn btn-default" onclick='$("#wsaddr").val("")'>清空</button>
		</div>
		<div class="row">
			<div id="output" style="border:1px solid #ccc;height:365px;overflow: auto;margin: 20px 0;"></div>
			<input type="text" id='message' class="form-control" style='width:810px' placeholder="待发信息" onkeydown="en(event);">
			<span class="input-group-btn">
				<button class="btn btn-default" type="button" onclick="doSend();">发送</button>
			</span>
			</div>
		</div>
	</body>		
		
		<script src="https://code.jquery.com/jquery-3.1.1.min.js"></script>
		<script language="javascript" type="text/javascript">
			function formatDate(now) {
				var year = now.getFullYear();
				var month = now.getMonth() + 1;
				var date = now.getDate();
				var hour = now.getHours();
				var minute = now.getMinutes();
				var second = now.getSeconds();
				return year + "-" + (month = month < 10 ? ("0" + month) : month) + "-" + (date = date < 10 ? ("0" + date) : date) +
					" " + (hour = hour < 10 ? ("0" + hour) : hour) + ":" + (minute = minute < 10 ? ("0" + minute) : minute) + ":" + (
						second = second < 10 ? ("0" + second) : second);
			}
			var output;
			var websocket;
 
			function init() {
				output = document.getElementById("output");
				testWebSocket();
			}
 
			function addsocket() {
				var wsaddr = $("#wsaddr").val();
				if (wsaddr == '') {
					alert("请填写websocket的地址");
					return false;
				}
				StartWebSocket(wsaddr);
			}
 
			function closesocket() {
				websocket.close();
			}
 
			function StartWebSocket(wsUri) {
				websocket = new WebSocket(wsUri);
				websocket.onopen = function(evt) {
					onOpen(evt)
				};
				websocket.onclose = function(evt) {
					onClose(evt)
				};
				websocket.onmessage = function(evt) {
					onMessage(evt)
				};
				websocket.onerror = function(evt) {
					onError(evt)
				};
			}
 
			function onOpen(evt) {
				writeToScreen("<span style='color:red'>连接成功,现在你可以发送信息啦!!!</span>");
			}
 
			function onClose(evt) {
				writeToScreen("<span style='color:red'>websocket连接已断开!!!</span>");
				websocket.close();
			}
 
			function onMessage(evt) {
				writeToScreen('<span style="color:blue">服务端回应&nbsp;' + formatDate(new Date()) + '</span><br/><span class="bubble">' +
					evt.data + '</span>');
			}
 
			function onError(evt) {
				writeToScreen('<span style="color: red;">发生错误:</span> ' + evt.data);
			}
 
			function doSend() {
				var message = $("#message").val();
				if (message == '') {
					alert("请先填写发送信息");
					$("#message").focus();
					return false;
				}
				if (typeof websocket === "undefined") {
					alert("websocket还没有连接,或者连接失败,请检测");
					return false;
				}
				if (websocket.readyState == 3) {
					alert("websocket已经关闭,请重新连接");
					return false;
				}
				console.log(websocket);
				$("#message").val('');
				writeToScreen('<span style="color:green">你发送的信息&nbsp;' + formatDate(new Date()) + '</span><br/>' + message);
				websocket.send(message);
			}
 
			function writeToScreen(message) {
				var div = "<div class='newmessage'>" + message + "</div>";
				var d = $("#output");
				var d = d[0];
				var doScroll = d.scrollTop == d.scrollHeight - d.clientHeight;
				$("#output").append(div);
				if (doScroll) {
					d.scrollTop = d.scrollHeight - d.clientHeight;
				}
			}
 
 
			function en(event) {
				var evt = evt ? evt : (window.event ? window.event : null);
				if (evt.keyCode == 13) {
					doSend()
				}
			}
		</script>
 
</html>

双击打开这个html文件,页面的功能如下,websocket的通信地址要以ws://开头,因为我的springboot启动在8081端口,所以我的地址就是 ws://localhost:8081/api/websocket/{连接id},websocket前面加了api是前面在yml文件中配置了context-path,没有配置可以去掉。实际项目中,将连接id换成唯一的用户id即可向指定用户发送消息。
在这里插入图片描述

效果如下
在这里插入图片描述

这时候服务器控制台输出为
在这里插入图片描述

到此,SpringBoot搭建WebSocket就已经完成,还有什么问题可以私信作者~

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

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

相关文章

STM32读保护的解除和出现的原因,使用串口和ST-LINK Utility解除读保护

STM32读保护的解除和出现的原因&#xff0c;使用串口和ST-LINK Utility解除读保护STM32读保护读保护保护出现的现象出现的原因读保护解决方法1、重新下载CH340驱动2、FlyMcu串口下载软件接触读保护使用STM32 ST-LINK Utility软件解除读保护注意STM32读保护 读保护保护出现的现…

4-(1,2,2-三苯基乙烯基)苯甲醛;​CHO醛基偶联AIE材料

中文名 4-(1,2,2-三苯基乙烯基)苯甲醛 英文名 4-(1,2,2-triphenylvinyl)benzaldehyde 4-(1,2,2-三苯基乙烯基)苯甲醛物理化学性质 分子式 C27H20O 分子量 360.455 质量 360.151428 AIE聚集诱导发光材料的特点&#xff1a; 1.在固态下有强发光特性&#xff08;粉末或高浓度&am…

【Linux03-基本工具之GCC】Linux下的C语言编译器

前言 接上篇&#xff0c;继续学习基本工具。 三、gcc 是什么 Linux下的C语言编译器&#xff08;C的编译器是g&#xff0c;用法选项基本一样&#xff09;。 既然是编译器&#xff0c;我们就再来加点餐…… 链接其实分为两种类型&#xff1a;静态链接和动态链接&#xff0…

内切相减原理绘制CAD图形

CAD中的内切相减的原理你们知道是什么意思吗&#xff1f;这个就需要用CAD实际图形的绘制过程来理解了&#xff0c;这个图形就用到了这个&#xff0c;还用到了CAD直线、修剪、圆等命令共同绘制出来。 目标图形 操作步骤 1.使用CAD直线命令绘制一条长度30的垂直直线AC&#xff…

自定义 HandlerMethodArgumentResolver 怎么和默认HandlerMethodArgumentResolver进行隔离的?

工作中&#xff0c;想对一些参数进行非空默认值的操作。 DefaultValueIfNull("1000") 这个就是&#xff0c;如果为null&#xff0c;那么就默认值填1000。 操作起来。 1.自定义HandlerMethodArgumentResolver Component public class DefaultValueIfNullResolver…

seata-server-1.5.2的部署

使用的是nacos和mysql数据库&#xff0c;简单部署在Win10上&#xff0c;Linux上配置修改相同&#xff0c;启动命令不同。 找到 seata-server-1.5.2\seata\conf目录下的application.yml和application.example.yml 编辑 application.yml&#xff0c;原始版本如下&#xff1a; s…

RocketMq: Windows环境-单机部署和伪集群、集群部署

关于默认端口 broker的默认端口有3个,10911, 10912, 10909。 10911是remotingServer使用的监听端口,remotingServer主要处理以下三类消息: producer发送的消息 conumser在消费失败或者消费超时发送的消息 consumer拉取消息10912是主broker用于监听从broker请求…

制作路由器openwrt安装及配置

Openwrt软路由安装配置1、 Openwrt软路由设备信息&#xff1a; <1>登录信息&#xff1a; IP&#xff1a;10.4.1.1 账号&#xff1a;root<2>设备配置&#xff1a; 主机名 Openwrt 型号 ASUSTeK COMPUTER INC. Z9PA-U8 Series CPU 型号 Intel(R) Xeon(R) …

YOLO系列目标检测算法——PP-YOLO

YOLO系列目标检测算法目录 - 文章链接 YOLO系列目标检测算法总结对比- 文章链接 YOLOv1- 文章链接 YOLOv2- 文章链接 YOLOv3- 文章链接 YOLOv4- 文章链接 Scaled-YOLOv4- 文章链接 YOLOv5- 文章链接 YOLOv6- 文章链接 YOLOv7- 文章链接 PP-YOLO- 文章链接…

爬虫之selenium

目录 selenium介绍 基本使用 selenium用法 元素操作 等待元素被加载 元素各项属性 执行js代码 切换选项卡 浏览器前进后退 无界面浏览器 xpath的使用 简单介绍 selenium中使用 异常处理 登录获取cookie保存 动作链 打码平台使用(验证码破解) selenium介绍 由…

JavaScript -- 正则表达式及示例代码介绍

文章目录正则表达式1 正则表达式的介绍2 创建正则表达式3 通过整个表达式检查字符串是否符合规则4 正则表达式的基本语法5 提取符合规则的字符串6 例子正则表达式 1 正则表达式的介绍 正则表达式用来定义一个规则通过这个规则计算机可以检查一个字符串是否符合规则或者将字符…

python PyQt6 常用操作以及常见问题解决

因为需求需要写一个简单的Python GUI界面&#xff0c;期间遇到了一些问题&#xff0c;在这里记录下 安装PyQt6: pip install pyqt6使用QTDesigner绘制界面&#xff1a; 我使用Anaconda下载的pyqt6里已经自带了两种工具&#xff0c;下面只需要把工具导入到pycharm中&#xff0…

[附源码]计算机毕业设计旅游网的设计与实现Springboot程序

项目运行 环境配置&#xff1a; Jdk1.8 Tomcat7.0 Mysql HBuilderX&#xff08;Webstorm也行&#xff09; Eclispe&#xff08;IntelliJ IDEA,Eclispe,MyEclispe,Sts都支持&#xff09;。 项目技术&#xff1a; SSM mybatis Maven Vue 等等组成&#xff0c;B/S模式 M…

『Python学习笔记』使用Cython编程语言编译python文件

使用Cython编程语言编译python文件 文章目录一. Cython简介一. Cython编译2.1. 编译过程2.2. 环境安装2.3. disutils库2.4. 引入C源文件三. 总结参考文献一. Cython简介 Cython官网地址&#xff1a;https://cython.org/Cython的下载和安装&#xff1a;https://pypi.org/projec…

嵌入式分享合集117

一、获取STM32代码运行时间的技巧 测试代码的运行时间的两种方法&#xff1a; 使用单片机内部定时器&#xff0c;在待测程序段的开始启动定时器&#xff0c;在待测程序段的结尾关闭定时器。为了测量的准确性&#xff0c;要进行多次测量&#xff0c;并进行平均取值。 借助示波器…

NFV概述

NFV&#xff08;网络功能虚拟化&#xff09;是指利用虚拟化技术在标准化的通用IT设备&#xff08;X86服务器&#xff0c;存储和交换设备&#xff09;上实现各种网络功能。NFV的目标是取代通信网络中私有、专用和封闭的网元&#xff0c;实现统一通用硬件平台业务逻辑软件的开放架…

争议不断的AI绘画,靠什么成为了顶流?

今年以来&#xff0c;AIGC迅速崛起。所谓AIGC&#xff0c;即AI-Generated Content&#xff0c;指的是利用人工智能来生成内容&#xff0c;被认为是继专业产出内容&#xff08;PGC&#xff09;、用户产出内容&#xff08;UGC&#xff09;后的新型内容创作方式。不久前掀起热议的…

客户需求太多,如何有效沟通完成项目?

1、向客户明确&#xff1a;工作量、时间与质量的关系 需要想客户明确&#xff0c;某时间内在保障开发质量的前提下&#xff0c;实际的工作量。如果加大工作量&#xff0c;在赶工情况下&#xff0c;开发质量无法保障。如要保障开发质量&#xff0c;开发时间会延长&#xff0c;那…

【电力系统】基于Matlab实现风电光伏概率潮流计算

✅作者简介&#xff1a;热爱科研的Matlab仿真开发者&#xff0c;修心和技术同步精进&#xff0c;matlab项目合作可私信。 &#x1f34e;个人主页&#xff1a;Matlab科研工作室 &#x1f34a;个人信条&#xff1a;格物致知。 更多Matlab仿真内容点击&#x1f447; 智能优化算法 …

threejs官方demo学习(1):animation

前言 之前的threejs入门视频教学已经学习完了&#xff0c;下面会陆续学习官方demo。官方网址太卡了建议在本地进行搭建&#xff0c;具体见&#xff1a;threejs视频教程学习&#xff08;1&#xff09;&#xff1a;本地搭建threeJS官网、渲染第一个场景 官方的例子都是html格式…