SpringBoot 整合WebSocket 简单实战案例

news2024/11/25 19:36:29

先是pom.xml添加依赖:


		<dependency>

			<groupId>org.springframework.boot</groupId>

			<artifactId>spring-boot-starter-websocket</artifactId>

		</dependency>

PS:application.properties不需要添加任何配置 ,我只设置了一下服务server.port=8083

接着,创建节点配置类WebSocketStompConfig.java:


import org.springframework.context.annotation.Bean;

import org.springframework.context.annotation.Configuration;

import org.springframework.web.socket.server.standard.ServerEndpointExporter;





@Configuration

public class WebSocketStompConfig {

    //这个bean的注册,用于扫描带有@ServerEndpoint的注解成为websocket  ,如果你使用外置的tomcat就不需要该配置文件

    @Bean

    public ServerEndpointExporter serverEndpointExporter()

    {

        return new ServerEndpointExporter();

    }

}



然后是WebSocket配置类,WebSocket.java:

(这里面包含这单独发送消息,群发,监听上下线等等方法)


import java.io.IOException;

import java.util.ArrayList;

import java.util.List;

import java.util.Map;

import java.util.Set;

import java.util.concurrent.ConcurrentHashMap;

import java.util.concurrent.CopyOnWriteArraySet;

import java.util.concurrent.atomic.AtomicInteger;

import javax.websocket.OnClose;

import javax.websocket.OnError;

import javax.websocket.OnMessage;

import javax.websocket.OnOpen;

import javax.websocket.Session;

import javax.websocket.server.PathParam;

import javax.websocket.server.ServerEndpoint;



import com.alibaba.fastjson.JSON;

import com.alibaba.fastjson.JSONObject;

import com.google.common.collect.Maps;

import org.slf4j.Logger;

import org.slf4j.LoggerFactory;

import org.springframework.stereotype.Component;

/**

 * @Author:JCccc

 * @Description:

 * @Date: created in 15:56 2019/5/13

 */



@Component

@ServerEndpoint(value = "/connectWebSocket/{userId}")

public class WebSocket {



    private Logger logger = LoggerFactory.getLogger(this.getClass());

    /**

     * 在线人数

     */

    public static int onlineNumber = 0;

    /**

     * 以用户的姓名为key,WebSocket为对象保存起来

     */

    private static Map<String, WebSocket> clients = new ConcurrentHashMap<String, WebSocket>();

    /**

     * 会话

     */

    private Session session;

    /**

     * 用户名称

     */

    private String userId;

    /**

     * 建立连接

     *

     * @param session

     */

    @OnOpen

    public void onOpen(@PathParam("userId") String userId, Session session)

    {

        onlineNumber++;

        logger.info("现在来连接的客户id:"+session.getId()+"用户名:"+userId);

        this.userId = userId;

        this.session = session;

      //  logger.info("有新连接加入! 当前在线人数" + onlineNumber);

        try {

            //messageType 1代表上线 2代表下线 3代表在线名单 4代表普通消息

            //先给所有人发送通知,说我上线了

            Map<String,Object> map1 = Maps.newHashMap();

            map1.put("messageType",1);

            map1.put("userId",userId);

            sendMessageAll(JSON.toJSONString(map1),userId);



            //把自己的信息加入到map当中去

            clients.put(userId, this);

 logger.info("有连接关闭! 当前在线人数" + clients.size());

            //给自己发一条消息:告诉自己现在都有谁在线

            Map<String,Object> map2 = Maps.newHashMap();

            map2.put("messageType",3);

            //移除掉自己

            Set<String> set = clients.keySet();

            map2.put("onlineUsers",set);

            sendMessageTo(JSON.toJSONString(map2),userId);

        }

        catch (IOException e){

            logger.info(userId+"上线的时候通知所有人发生了错误");

        }







    }



    @OnError

    public void onError(Session session, Throwable error) {

        logger.info("服务端发生了错误"+error.getMessage());

        //error.printStackTrace();

    }

    /**

     * 连接关闭

     */

    @OnClose

    public void onClose()

    {

        onlineNumber--;

        //webSockets.remove(this);

        clients.remove(userId);

        try {

            //messageType 1代表上线 2代表下线 3代表在线名单  4代表普通消息

            Map<String,Object> map1 = Maps.newHashMap();

            map1.put("messageType",2);

            map1.put("onlineUsers",clients.keySet());

            map1.put("userId",userId);

            sendMessageAll(JSON.toJSONString(map1),userId);

        }

        catch (IOException e){

            logger.info(userId+"下线的时候通知所有人发生了错误");

        }

        //logger.info("有连接关闭! 当前在线人数" + onlineNumber);

          logger.info("有连接关闭! 当前在线人数" + clients.size());

    }



    /**

     * 收到客户端的消息

     *

     * @param message 消息

     * @param session 会话

     */

    @OnMessage

    public void onMessage(String message, Session session)

    {

        try {

            logger.info("来自客户端消息:" + message+"客户端的id是:"+session.getId());



            System.out.println("------------  :"+message);



            JSONObject jsonObject = JSON.parseObject(message);

            String textMessage = jsonObject.getString("message");

            String fromuserId = jsonObject.getString("userId");

            String touserId = jsonObject.getString("to");

            //如果不是发给所有,那么就发给某一个人

            //messageType 1代表上线 2代表下线 3代表在线名单  4代表普通消息

            Map<String,Object> map1 = Maps.newHashMap();

            map1.put("messageType",4);

            map1.put("textMessage",textMessage);

            map1.put("fromuserId",fromuserId);

            if(touserId.equals("All")){

                map1.put("touserId","所有人");

                sendMessageAll(JSON.toJSONString(map1),fromuserId);

            }

            else{

                map1.put("touserId",touserId);

                System.out.println("开始推送消息给"+touserId);

                sendMessageTo(JSON.toJSONString(map1),touserId);

            }

        }

        catch (Exception e){



            e.printStackTrace();

            logger.info("发生了错误了");

        }



    }





    public void sendMessageTo(String message, String TouserId) throws IOException {

        for (WebSocket item : clients.values()) {





            //    System.out.println("在线人员名单  :"+item.userId.toString());

            if (item.userId.equals(TouserId) ) {

                item.session.getAsyncRemote().sendText(message);



                break;

            }

        }

    }



    public void sendMessageAll(String message,String FromuserId) throws IOException {

        for (WebSocket item : clients.values()) {

            item.session.getAsyncRemote().sendText(message);

        }

    }



    public static synchronized int getOnlineCount() {

        return onlineNumber;

    }



}





接下来用一个HTML5 页面 index.html,连接当前的WebSocket节点,接/发消息, index.html:


<!DOCTYPE HTML>

<html>

<head>

    <title>Test My WebSocket</title>

</head>





<body>

TestWebSocket

<input  id="text" type="text" />

<button onclick="send()">SEND MESSAGE</button>

<button onclick="closeWebSocket()">CLOSE</button>

<div id="message"></div>

</body>



<script type="text/javascript">

    var websocket = null;





    //判断当前浏览器是否支持WebSocket

    if('WebSocket' in window){

        //连接WebSocket节点

        websocket = new WebSocket("ws://localhost:8083/connectWebSocket/001");

    }

    else{

        alert('Not support websocket')

    }





    //连接发生错误的回调方法

    websocket.onerror = function(){

        setMessageInnerHTML("error");

    };





    //连接成功建立的回调方法

    websocket.onopen = function(event){

        setMessageInnerHTML("open");

    }





    //接收到消息的回调方法

    websocket.onmessage = function(event){

        setMessageInnerHTML(event.data);

    }





    //连接关闭的回调方法

    websocket.onclose = function(){

        setMessageInnerHTML("close");

    }





    //监听窗口关闭事件,当窗口关闭时,主动去关闭websocket连接,防止连接还没断开就关闭窗口,server端会抛异常。

    window.onbeforeunload = function(){

        websocket.close();

    }





    //将消息显示在网页上

    function setMessageInnerHTML(innerHTML){

        document.getElementById('message').innerHTML += innerHTML + '<br/>';

    }





    //关闭连接

    function closeWebSocket(){

        websocket.close();

    }





    //发送消息

    function send(){

        var message = document.getElementById('text').value;

        websocket.send(message);

    }

</script>

</html>



为了演示多人接入,我们再弄一个index2.html:

好了,一切准备就绪,那么 把项目跑起来:

访问index.html模拟用户001连接websocket服务:

可以看到一上线,我们默认就推送了一条上线消息

接下来继续访问index2.html,模拟用户002也接入websocket:

此刻,我们模拟咱们服务器给客户推送消息,有群发和单独发送,我们一一实践:

单独发送,只需要调用websocket.java里面的 sendMessageTo方法:

那么我们来写个简单的推送信息接口,


    @Autowired

    WebSocket webSocket;





    @ResponseBody

    @GetMapping("/sendTo")

    public String sendTo(@RequestParam("userId") String userId,@RequestParam("msg") String msg) throws IOException {



        webSocket.sendMessageTo(msg,userId);



        return "推送成功";

    }



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

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

相关文章

元学习之模型诊断元学习(model-agnosticmeta-learning,MAML)

模型诊断元学习&#xff08;model-agnosticmeta-learning&#xff0c;MAML&#xff09;&#xff0c; 另一个是Reptile。这两个算法都是在 2017 年提出来的&#xff0c;而且都是基于梯度下降法进行优化 的。那我们最常用的学习算法是梯度下降&#xff0c;在梯度下降中&#xff0…

基于VUE的在线音乐播放管理系统

&#x1f449;文末查看项目功能视频演示获取源码sql脚本视频导入教程视频 1 、功能描述 基于VUE的在线音乐播放管理系统3是前后端分离项目&#xff0c;拥有两种角色 管理员&#xff1a;用户管理、收藏管理、歌手管理、歌曲管理、歌单管理、评论管理等 用户&#xff1a;登录注…

解锁生活密码,AI答案之书解决复杂难题

本文由 ChatMoney团队出品 介绍说明 “答案之书智能体”是您贴心的智慧伙伴&#xff0c;随时准备为您解答生活中的种种困惑。无论您在工作中遭遇瓶颈&#xff0c;还是在情感世界里迷失方向&#xff0c;亦或是对个人成长感到迷茫&#xff0c;它都能倾听您的心声&#xff0c;并给…

Vscode python无法转到函数定义

今天上午换了电脑&#xff0c;使用Vscode发现找不到对应的函数定义了。 使用了网上的全部教程。一点用没有。重启电脑&#xff0c;重启Vscode也没有作用。最后通过重装vscode&#xff0c;解决问题。&#xff08;也不知道Vscode什么毛病&#xff09; 重点语句&#xff1a; 去官网…

四款免费视频剪辑工具使用感受与优劣势总结

在如今这个视频内容如火如荼的时代&#xff0c;如何快速高效地完成视频剪辑成为许多人关心的问题&#xff1b;今天&#xff0c;我们就来轻松愉快地聊一聊本人常用的四款免费的视频剪辑工具&#xff1b;这四款工具各有千秋&#xff0c;让我们一起来看看它们的使用感受和优劣势吧…

Java代码审计篇 | ofcms系统审计思路讲解 - 篇3 | 文件上传漏洞审计

文章目录 0. 前言1. 文件上传代码审计【有1处】1.1 可疑点1【无漏洞】1.1.1 直接搜索upload关键字1.1.2 选择第一个&#xff0c;点进去分析一下1.1.3 分析this.getFile()方法1.1.4 分析new MultipartRequest(request, uploadPath)1.1.5 分析isSafeFile()方法1.1.6 分析request.…

连锁管理系统如何兼批发和零售 连锁收银系统如何配合做好财务

在现代零售环境中&#xff0c;信息化管理系统对连锁企业的运营至关重要。连锁管理系统通过先进的信息技术解决了批发和零售中的众多挑战&#xff0c;同时为财务管理提供了有力支持。商淘云分享如何提高连锁企业的运营效率和财务管理水平&#xff0c;大家点赞收藏。 1、统筹批发…

算法知识点————背包问题

万能头文件#include<bits/stdc.h> 01 背包 定义&#xff1a; 物品只能用1次。01对应选还是不选第i个物品 .N个物品、V容量的最大价值。 思路&#xff1a; &#xff08;1&#xff09;f[ i ] [j] 表示前i个物品容量j的最大价值。 &#xff08;2&#xff09;当前背包容量…

在WPF中使用矢量图标

使用SVG/Path WPF支持绘图&#xff0c;在WPF中定义了一些基本图形&#xff0c;如Line、Rectangle、Ellipse、Path等 其中Path支持使用路径来进行绘图&#xff0c;所以大部分的图标可以通过Path来进行绘制。 因为本文主要是介绍矢量图标&#xff0c;所以不对绘图进行详细介绍…

opencv之图像梯度

图像梯度 图像梯度计算的是图像变化的速度。对于图像的边缘部分&#xff0c;其灰度值变化较大&#xff0c;梯度值也较大&#xff1b;相反&#xff0c;对于图像中比较平滑的部分&#xff0c;其灰度值变化较小&#xff0c;相应的梯度值也较小。一般情况下&#xff0c;图像梯度计…

重命名工具 | Advanced Renamer v4.03 绿色版

Advanced Renamer 是一款专为 Windows 平台设计的强大文件批量重命名工具。它提供了多种重命名方法&#xff0c;包括指定新文件名、改变大小写、移动字符、移除字符串、重编文件名序号、替换字符、添加内容、使用列表或列表文件替换文件名、交换字符位置、去除头部或尾部多余空…

百度Apollo打通与ROS的通信,扩展自动驾驶系统生态

技术文档&#xff5c;打通与ROS的通信&#xff0c;扩展自动驾驶系统生态_Apollo开发者社区 (baidu.com)

-顺序表-

一、小概述 二、代码解析 下面利用顺序表实现增删改查的功能&#xff1a; //动态顺序表 #include <iostream> using namespace std; typedef int SLDataType;//适用于多种类型 typedef struct SeqList {SLDataType* a;//数组int size;//表示数组中存储了多少个元素int …

【车载开发系列】ParaSoft单元测试环境配置(一)

【车载开发系列】ParaSoft单元测试环境配置(一) ParaSoft单元测试环境配置 【车载开发系列】ParaSoft单元测试环境配置(一)一. 什么是bdf文件二. bdf文件构成三. 新规做成bdf文件四. 导入bdf文件创建测试工程五. 获取编译器信息六. 新增自定义编译器Step1:打开向导Step2:…

Xcode 16 RC (16A242) 发布下载,正式版下周公布

Xcode 16 RC (16A242) - Apple 平台 IDE IDE for iOS/iPadOS/macOS/watchOS/tvOS/visonOS 请访问原文链接&#xff1a;https://sysin.org/blog/apple-xcode-16/&#xff0c;查看最新版。原创作品&#xff0c;转载请保留出处。 作者主页&#xff1a;sysin.org Xcode 16 的新功…

【拓扑系列】拓扑排序

【拓扑系列】拓扑排序 前言认识有向无环图认识AOV网&#xff1a;顶点活动图拓扑排序 1. 课程表1.1 题目来源1.2 题目描述1.3 题目解析 2. 课程表 II2.1 题目来源2.2 题目描述2.3 题目解析 3. LCR 114. 火星词典3.1 题目来源3.2 题目描述3.3 题目解析 前言 认识有向无环图 图中…

AI基础 L13 Constraint Satisfaction Problems I约束满足问题

Defining Constraint Satisfaction Problems A constraint satisfaction problem (CSP) consists of three components, X, D, and C: • X is a set of variables, {X1, . . . , Xn}. • D is a set of domains, {D1, . . . , Dn}, one for each variable • C is a set of co…

STM32+ESP8266 WiFi连接机智云平台APP远程控制教程

本文档将介绍如何用STM32ESP8266 WiFi模块从零开始连接上机智云&#xff0c;并通过APP进行远程控制。 机智云官网&#xff1a;机智云|智能物联网操作系统 (gizwits.com) 准备&#xff1a;STM32、ESP8266、手机、可上网的WiFi。 1.创建设备 1.1 注册登陆 请自行注册账号并登陆…

脱离八股文,真实开发中的延时处理需求实现思路(超时订单处理为例)

前言 咱们聊聊那些在开发过程中经常遇到的延时处理需求吧。比如说&#xff0c;网购时那些迟迟不付款的订单&#xff0c;或者是社交软件里那些需要稍后处理的消息&#xff0c;再或者是金融交易中那些需要等待确认的交易。这些都是咱们得搞定的活儿。 不过&#xff0c;很多时候&a…

JS面试真题 part3

JS面试真题 part3 11、bind、call、apply区别&#xff1f;如何实现一个bind12、JavaScript中执行上下文和执行栈是什么13、说说JavaScript中的事件模型14、解释下什么是事件代理&#xff1f;应用场景&#xff1f;15、说说你对闭包的理解&#xff1f;闭包使用场景 11、bind、cal…