jjzjj

springboot Socket 通信

timeguys 2025-02-11 原文

一、引入依赖:

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

二、准备工具类:

/**
 * @author WeiDaPang
 */
@Configuration
public class ScheduledConfiguration{
    @Bean
    public TaskScheduler taskScheduler(){
        ThreadPoolTaskScheduler taskScheduler = new ThreadPoolTaskScheduler();
        taskScheduler.setPoolSize(10);
        taskScheduler.initialize();
        return taskScheduler;
    }
}
/**
 * WebSocket 配置
 * @author WeiDaPang
 */
@EnableWebSocket
@Configuration
public class WebSocketConfig implements WebSocketConfigurer {
    @Bean
    public ServerEndpointExporter serverEndpointExporter(){
        return new ServerEndpointExporter();
    }

    /**
     *  注入WebSocket的处理器
     */
    @Resource
    private WebSocketServer  webSocketServer;

    @Override
    public void registerWebSocketHandlers(WebSocketHandlerRegistry registry) {
        // webSocketDemo 为前端请求的地址,前端具体地址组成结构为:ws://ip:接口启动的端口/webSocketDemo
        registry.addHandler(webSocketServer,"webSocketByNotice")
                .setAllowedOrigins("*");
    }
}
/**
 * @author Da.Pang
 * 2022/11/8 18:57
 */
public class WebSocketEntity {

    private String userId;
    private WebSocketSession webSocketSession;

    public WebSocketEntity(String userId , WebSocketSession webSocketSession){
        this.userId = userId;
        this.webSocketSession = webSocketSession;
    }

    public String getUserId() {
        return userId;
    }

    public WebSocketEntity setUserId(String userId) {
        this.userId = userId;
        return this;
    }

    public WebSocketSession getWebSocketSession() {
        return webSocketSession;
    }

    public WebSocketEntity setWebSocketSession(WebSocketSession webSocketSession) {
        this.webSocketSession = webSocketSession;
        return this;
    }
}
/**
 * @author WeiDaPang
 */
@Component
public class WebSocketServer extends AbstractWebSocketHandler {

    private static final Logger logger = LoggerFactory.getLogger(WsService.class);

    @Resource
    private WsService  wsService;

    /**
     * 连接成功后触发
     */
    @Override
    public void afterConnectionEstablished(WebSocketSession session) throws Exception {
        logger.info("...[客户端连接成功]...");
        // 客户端建立连接,将客户端的session对象加入到WebSocketSessionManager的sessionGroup中
        WebSocketSessionManager.add(session);
        //将连接结果返回给客户端
        wsService.sendMsg(session,session.getId()+" 连接成功"+ LocalDateTime.now());
    }

    /**
     * 关闭socket连接后触发
     */
    @Override
    public void afterConnectionClosed(WebSocketSession session, CloseStatus status) throws Exception {
        logger.info("...【客户端关闭连接成功】...");
        // 关闭连接,从WebSocketSessionManager的sessionGroup中移除连接对象
        WebSocketSessionManager.removeAndClose(session);
    }

    /**
     * 接收客户端发送的文本消息
     */
    @Override
    protected void handleTextMessage(WebSocketSession session, TextMessage message) throws Exception {
        logger.info("{ 客户端发送:}"+message.getPayload());
        //将连接结果返回给客户端
        wsService.sendMsg(session,message.getPayload());
    }

    /**
     * 接收客户端发送的二进制消息
     */
    @Override
    protected void handleBinaryMessage(WebSocketSession session, BinaryMessage message) throws Exception {
        logger.info("{ 客户端二进制发送: }"+message.getPayload());
    }

    /**
     * 异常处理
     */
    @Override
    public void handleTransportError(WebSocketSession session, Throwable exception) throws Exception {
        System.out.println();
        logger.error("*** socket异常: "+exception.getMessage(),exception);
        // 出现异常则关闭连接,从WebSocketSessionManager的sessionGroup中移除连接对象
        WebSocketSessionManager.removeAndClose(session);
    }
}
/**
 * @author WeiDaPang
 */
public class WebSocketSessionManager {
    private static final Logger logger = LoggerFactory.getLogger(WebSocketSessionManager.class);
    /**
     * 保存连接对象的 session 到集合中
     */
    public  static ConcurrentHashMap<String, WebSocketEntity> sessionGroup = new ConcurrentHashMap<>();


    /**
     * 添加 session 到集合中
     * @param session 连接对象的session
     */
    public static void add(WebSocketSession session) {
        logger.info(String.valueOf(session.getUri()));
        sessionGroup.put(session.getId(), new WebSocketEntity(getUserIdBySession(session), session));
    }

    /**
     * 从集合中删除 session,会返回删除的 session
     * @param key 通过session.getId()得到
     */
    public static WebSocketSession remove(String key) {
        return sessionGroup.remove(key).getWebSocketSession();
    }

    /**
     * 从集合中删除 session,会返回删除的 session
     * @param session 连接对象的session
     */
    public static WebSocketSession remove(WebSocketSession session) {
        return sessionGroup.remove(session.getId()).getWebSocketSession();
    }

    /**
     * 删除并关闭 连接
     * @param key 通过session.getId()得到
     */
    public static void removeAndClose(String key) {
        WebSocketSession session = remove(key);
        if (session != null) {
            try {
                // 关闭连接
                session.close();
            } catch (IOException e) {
                logger.error("关闭出现异常处理",e);
                e.printStackTrace();
            }
        }
    }

 	/**
     * 删除并关闭 连接
     * @param session 连接对象的session
     */
    public static void removeAndClose(WebSocketSession session) {
        WebSocketSession session2 = remove(session);
        if (session2 != null) {
            try {
                // 关闭连接
                session.close();
            } catch (IOException e) {
                logger.error("关闭出现异常处理",e);
                e.printStackTrace();
            }
        }
    }

    /**
     * 从集合中获得 session
     * @param key 通过session.getId()得到
     */
    public static WebSocketSession get(String key) {
        return sessionGroup.get(key).getWebSocketSession();
    }


    /**
     * [描述] 从ws地址中获取用户id
     * 格式:ws://IP:端口/webSocketByNotice?userId=10000000
     */
    public static String getUserIdBySession(WebSocketSession session){
        String uri = Objects.requireNonNull(session.getUri()).toString();
        uri =  uri.substring(uri.indexOf("?")+1);
        uri =  uri.replace("userId=","").trim();
        return uri;
    }

}
/**
 * @author WeiDaPang
 */
@Service
public class WsService {


    /**
     * 发送消息给指定客户端
     * @param session 客户端session
     * @param text 发送消息的内容
     */
    public synchronized  void sendMsg(WebSocketSession session, String text) throws IOException {
        session.sendMessage(new TextMessage(text));
        Notice notice = new Notice();
        notice.setNoticeTitle("消息标题");
        notice.setNoticeContent(text);
        notice.setCreateTime(new Date());
        // 发送一条消息 就广播给所有人
        broadcastMsg(JSONUtil.toJsonStr(notice));
    }
    /**
     * 发送消息给指定客户端
     * @param session 客户端session
     * @param notice 通知
     */
    public synchronized  void sendMsgByNotice(WebSocketSession session, Notice notice) throws IOException {
        session.sendMessage(new TextMessage(JSONUtil.toJsonStr(notice)));
    }



    /**
     * 发送消息给所有客户端,客户端的session必须在WebSocketSessionManager的sessionGroup中
     * @param text 发送消息的内容
     */
    public synchronized void broadcastMsg(String text) throws IOException {
        for (WebSocketEntity entity: WebSocketSessionManager.sessionGroup.values()) {
            entity.getWebSocketSession().sendMessage(new TextMessage(text));
        }
    }

}

三、测试html页面代码:

<!DOCTYPE HTML>
<html lang="UTF-8">
<head>
    <title>WebSocket 测试页面</title>
</head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<body>
<input id="text" type="text" />
<button onclick="send()">发送消息</button>
<button onclick="closeWebSocket()">关闭webScoket连接</button>
<div id="message"></div>
</body>

<script type="text/javascript">
    // 连接到WebSocket的url地址。格式为  ws://ip:接口启动的端口/webSocketDemo
    // 如果 sping boot 配置文件中中配置了server.servlet.context-path ,则格式为: ws://ip:接口启动的端口/server.servlet.context-path的名称/webSocketDemo
    // 此处demo中 sping boot 配置了server.servlet.context-path 为 webSocketDemoApi
    let connectionUrl = "ws://127.0.0.1:808/webSocketByNotice?userId=10000001"
    let ws = null;
    //判断当前浏览器是否支持WebSocket
    if ('WebSocket' in window) {
        ws = new WebSocket(connectionUrl);
    }else {
        alert('当前浏览器不支持 websocket')
    }

    //连接发生错误的回调方法
    ws.onerror = function () {
        setMessageInnerHTML("WebSocket连接发生错误");
    };

    //连接成功建立的回调方法
    ws.onopen = function(event) {
        console.log("ws调用连接成功回调方法")
        //ws.send("")
    }
    //接收到消息的回调方法
    ws.onmessage = function(message) {
        console.log("接收消息:" + message.data);
        if (typeof(message.data) == 'string') {
            setMessageInnerHTML(message.data);
        }
    }
    //ws连接断开的回调方法
    ws.onclose = function(e) {
        console.log("ws连接断开")
        //console.log(e)
        setMessageInnerHTML("ws close");
    }

    //将消息显示在网页上
    function setMessageInnerHTML(innerHTML) {
        console.log(innerHTML)
        document.getElementById('message').innerHTML += '接收的消息:' + innerHTML + '<br/>';
    }

    //关闭连接
    function closeWebSocket() {
        ws.close();
    }


    //发送消息
    function send(msg) {
        if(!msg){
            msg = document.getElementById('text').value;
            console.log(msg)
            document.getElementById('message').innerHTML += "发送的消息:" + msg + '<br/>';
            ws.send(msg);
        }
    }
</script>

四、当socket和定时任务同时被引入自己的项目报错:

由于我的项目同时引入了
socket和定时任务,所以启动时候会报错,解决方式就是为socket创建一个【TaskScheduler】的bean。(第一个工具类就是解决这个问题的)

有关springboot Socket 通信的更多相关文章

  1. ruby-on-rails - Rails 应用程序之间的通信 - 2

    我构建了两个需要相互通信和发送文件的Rails应用程序。例如,一个Rails应用程序会发送请求以查看其他应用程序数据库中的表。然后另一个应用程序将呈现该表的json并将其发回。我还希望一个应用程序将存储在其公共(public)目录中的文本文件发送到另一个应用程序的公共(public)目录。我从来没有做过这样的事情,所以我什至不知道从哪里开始。任何帮助,将不胜感激。谢谢! 最佳答案 无论Rails是什么,几乎所有Web应用程序都有您的要求,大多数现代Web应用程序都需要相互通信。但是有一个小小的理解需要你坚持下去,网站不应直接访问彼此

  2. MIMO-OFDM无线通信技术及MATLAB实现(1)无线信道:传播和衰落 - 2

     MIMO技术的优缺点优点通过下面三个增益来总体概括:阵列增益。阵列增益是指由于接收机通过对接收信号的相干合并而活得的平均SNR的提高。在发射机不知道信道信息的情况下,MIMO系统可以获得的阵列增益与接收天线数成正比复用增益。在采用空间复用方案的MIMO系统中,可以获得复用增益,即信道容量成倍增加。信道容量的增加与min(Nt,Nr)成正比分集增益。在采用空间分集方案的MIMO系统中,可以获得分集增益,即可靠性性能的改善。分集增益用独立衰落支路数来描述,即分集指数。在使用了空时编码的MIMO系统中,由于接收天线或发射天线之间的间距较远,可认为它们各自的大尺度衰落是相互独立的,因此分布式MIMO

  3. 1个串口用1根线实现多机半双工通信+开机控制电路 - 2

    功能需求:主机使用一个串口,与两个从机进行双向通信,主机向从机发送数据,从机能够返回数据,由于结构限制,主机与从机之间只有3根线(电源、地、数据线),并且从机上没有设物理的电源开关,需要通过与主机连接的数据线来控制开机,总结如下:1、数据线只有1根2、能够双向通信3、主机能够控制从机开机4、主机可以单独向1个从机发数据,也可以同时向两个从机发送数据根据需求,设计出如下电路:工作原理分析:VCC_24V_IN、GND、LINE_L(LINE_R)三根线接线连接到从机,电源开启电路是从机内部的电源控制。开机的逻辑:*主机先上电,LINE_L因为主机的R1上拉而有高电平,使Q6导通,Q5的G极电压被

  4. ruby - 如何创建与帧缓冲区通信的 Ruby 应用程序? - 2

    我有一个RaspberryPiTFT7"触摸屏显示器,我想创建一个简单的应用程序来显示和输出系统数据(即CPU使用率、温度等)。我注意到目前常见的实现方法是使用pygame库输出到显示器连接到的帧缓冲区/dev/fb1。我想执行相同的操作,但使用Ruby,因为我更熟悉这门语言。有人可以为我指明正确的方向,让我知道如何开始吗?我查看了ruby​​game和gosu库,它们似乎能够做我想做的事情,即绘制屏幕,​​但我找不到任何关于如何将输出定向到的信息帧缓冲区本身。 最佳答案 rubycorelib有一个IO您应该能够使用该类将输出定向

  5. [蓝桥杯单片机]学习笔记——串口通信的基本原理与应用 - 2

    目录一、原理部分1、什么是串行通信(1)并行通信与串行通信(2)串行通信的制式(3)串行通信的主要方式  2、配置串口(1)SCON和PCON:串行口1的控制寄存器(2)SBUF:串行口数据缓冲寄存器 (3)AUXR:辅助寄存器​编辑(4)ES、PS:与串行口1中断相关的寄存器(5)波特率设置  3、串口框架编写二、程序案例一、原理部分1、什么是串行通信(1)并行通信与串行通信微控制器与外部设备的数据通信,根据连线结构和传送方式的不同,可以分为两种:并行通信和串行通信。并行通信:数据的各位同时发送与接收,每个数据位使用一条导线,这种方式传输快,但是需要多条导线进行信号传输。串行通信:数据一位一

  6. ruby - 后台工作人员在 Rails 应用程序中查看通信 - 2

    假设我有一个包含帖子的博客应用程序。创建帖子后,将创建一个工作人员来处理一些后台操作。我的情况是,在提交表单后我想显示某种加载消息(gif加载器等),当工作人员完成时我想隐藏加载消息并显示工作人员提供的一些数据。我的问题是,传达工作人员已完成工作并将其显示在用户前端的最佳方式是什么。worker回调看起来像这样defworker_finish#messagetheuserend 最佳答案 我认为您可能忽略了拥有后台工作人员的意义,基本上,您试图做的是弄巧成拙。--如果用户提交表单并且你在你的Controller中排队作业,只是为了让

  7. ruby - 为什么在使用 savon 进行 ruby​​ soap 通信时将 "wsdl"命名空间插入到操作名称中? - 2

    我正在尝试访问我无法控制的SOAP服务。其中一个操作称为ProcessMessage。我按照这个例子生成了一个SOAP请求,但我收到一条错误消息,指出该操作不存在。我将问题追溯到生成信封正文的方式。USER658e702d5feff1777a6c741847239eb5d6d86e482010-02-18T02:05:25Zpassword......ProcessMessage标签应该是:这就是示例Java应用程序生成它时的样子,并且可以正常工作。该标记是我的Ruby应用程序生成的内容与示例Java应用程序之间的唯一区别。有什么方法可以去掉那个标签前面的"wsdl:"命名空间并添加这

  8. QT 设计一个串口调试工具,用一个工程就能轻松解决,外加虚拟串口工具模拟调试,在日常工作中可类比模块间通信,非常详细建议收藏 - 2

    QT串口调试工具第一节虚拟串口工具安装第二节QT创建一个基于QWidget的项目第三节UI界面设计第三节项目头文件widget.h第四节项目实现文件widget.cpp第五节main函数第六节编译结果重点第七节使用QT打包程序,不安装QT的电脑可使用第一节虚拟串口工具安装-----------------------------------------下载所需工具---------------------------------------------------------------------链接:https://pan.baidu.com/s/1QkT36S4EnH2HEAhZ1TZ8

  9. linux远程开发——网络通信(客户端与服务器建立连接) - 2

    目录一、前言二、网络编程三要素1、IP地址1)IP地址概念2)通过IP地址访问CSDN官网3)本地回环IP地址127.0.0.12、端口号3、通信协议1)通信协议概念2)TCP和UDP三、网络通信基础编程1、编程流程2、建立本地服务器1)socket()初始化网络2)bind()函数3)listen()监听函数4)accept()函数5)服务器全部代码3、建立客户端4、客户端连接服务器测试一、前言        本文介绍网络编程的基础知识,使用VisualStudio2019在linux本地搭建一个服务器,将客户端与本地服务器连接起来,通过客户端向服务器发送信息,测试服务端能否收到信息。在编程

  10. ruby - 你如何在 Rake 任务之间进行通信? - 2

    假设我有一个需要编译一些文件的目标。该目标有另一个目标作为先决条件,即获取文件的目标。让我们这样说:task:obtaindo#obtainfilesfromsomewhereendtask:compile=>:obtaindo#docompilationend假设:obtain目标并不总是将文件放在同一个文件夹中。我如何将:compile传递给:obtain找到的路径?环境变量? 最佳答案 在我看来,使用ENV['something']更可取,因为如果你这样做(而不是$global或@instance变量),你可以将它们视为任务参

随机推荐