springboot整合WebSocket_springboot 整合websocket-程序员宅基地

技术标签: spring boot  初入职场  websocket  

springboot整合WebSocket

WebSocket通信过程

客户端构建一个websocket实例,并且为它绑定一个需要连接到的服务器地址,当客户端连接服务端的候,会向服务端发送一个http get报文,告诉服务端需要将通信协议切换到websocket,服务端收到http请求后将通信协议切换到websocket,同时发给客户端一个响应报文,返回的状态码为101,表示同意客户端协议转请求,并转换为websocket协议。以上过程都是利用http通信完成的,称之为websocket协议握手(websocket Protocol handshake),经过握手之后,客户端和服务端就建立了websocket连接,以后的通信走的都是websocket协议了。

1.pom文件添加依赖
       <!--webSocket-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-websocket</artifactId>
        </dependency>
2.启用Springboot对WebSocket的支持
package com.lby.websocket.config;

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

/**
 * @author Liby
 * @date 2022-04-25 16:18
 * @description:
 * @version:
 */
@Configuration
public class WebSocketConfig {

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

3.核心配置:WebSocketServer

因为WebSocket是类似客户端服务端的形式(采用ws协议),那么这里的WebSocketServer其实就相当于一个ws协议的Controller

  • @ ServerEndpoint 注解是一个类层次的注解,它的功能主要是将目前的类定义成一个websocket服务器端, 注解的值将被用于监听用户连接的终端访问URL地址,客户端可以通过这个URL来连接到WebSocket服务器端
  • 新建一个ConcurrentHashMap webSocketMap 用于接收当前userId的WebSocket,方便传递之间对userId进行推送消息。

下面是具体业务代码:

package com.lby.websocket.component;

import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.springframework.stereotype.Component;
import cn.hutool.core.util.StrUtil;
import javax.websocket.*;
import javax.websocket.server.PathParam;
import javax.websocket.server.ServerEndpoint;
import java.io.IOException;
import java.util.concurrent.ConcurrentHashMap;

/**
 * @author Liby
 * @date 2022-04-25 16:21
 * @description:
 * @version:
 */
@ServerEndpoint(value = "/websocket/{userId}")
@Component
public class WebSocket {
    private final static Logger logger = LogManager.getLogger(WebSocket.class);

    /**
     * 静态变量,用来记录当前在线连接数。应该把它设计成线程安全的
     */

    private static int onlineCount = 0;

    /**
     * concurrent包的线程安全Map,用来存放每个客户端对应的MyWebSocket对象
     */
    private static ConcurrentHashMap<String, WebSocket> webSocketMap = new ConcurrentHashMap<>();

    /**
     * 与某个客户端的连接会话,需要通过它来给客户端发送数据
     */

    private Session session;
    private String userId;


    /**
     * 连接建立成功调用的方法
     */
    @OnOpen
    public void onOpen(Session session, @PathParam("userId") String userId) {
        this.session = session;
        this.userId = userId;
        //加入map
        webSocketMap.put(userId, this);
        addOnlineCount();           //在线数加1
        logger.info("用户{}连接成功,当前在线人数为{}", userId, getOnlineCount());
        try {
            sendMessage(String.valueOf(this.session.getQueryString()));
        } catch (IOException e) {
            logger.error("IO异常");
        }
    }


    /**
     * 连接关闭调用的方法
     */
    @OnClose
    public void onClose() {
        //从map中删除
        webSocketMap.remove(userId);
        subOnlineCount();           //在线数减1
        logger.info("用户{}关闭连接!当前在线人数为{}", userId, getOnlineCount());
    }

    /**
     * 收到客户端消息后调用的方法
     *
     * @param message 客户端发送过来的消息
     */
    @OnMessage
    public void onMessage(String message, Session session) {
        logger.info("来自客户端用户:{} 消息:{}",userId, message);

        //群发消息
        /*for (String item : webSocketMap.keySet()) {
            try {
                webSocketMap.get(item).sendMessage(message);
            } catch (IOException e) {
                e.printStackTrace();
            }
        }*/
    }

    /**
     * 发生错误时调用
     *
     * @OnError
     */
    @OnError
    public void onError(Session session, Throwable error) {
        logger.error("用户错误:" + this.userId + ",原因:" + error.getMessage());
        error.printStackTrace();
    }

    /**
     * 向客户端发送消息
     */
    public void sendMessage(String message) throws IOException {
        this.session.getBasicRemote().sendText(message);
        //this.session.getAsyncRemote().sendText(message);
    }

    /**
     * 通过userId向客户端发送消息
     */
    public void sendMessageByUserId(String userId, String message) throws IOException {
        logger.info("服务端发送消息到{},消息:{}",userId,message);
     if(StrUtil.isNotBlank(userId)&&webSocketMap.containsKey(userId)){
         webSocketMap.get(userId).sendMessage(message);
     }else{
         logger.error("用户{}不在线",userId);
     }

    }

    /**
     * 群发自定义消息
     */
    public static void sendInfo(String message) throws IOException {
        for (String item : webSocketMap.keySet()) {
            try {
                webSocketMap.get(item).sendMessage(message);
            } catch (IOException e) {
                continue;
            }
        }
    }

    public static synchronized int getOnlineCount() {
        return onlineCount;
    }

    public static synchronized void addOnlineCount() {
        WebSocket.onlineCount++;
    }

    public static synchronized void subOnlineCount() {
        WebSocket.onlineCount--;
    }

}


4.测试controller
package com.lby.websocket.controller;

import com.lby.websocket.component.WebSocket;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import java.io.IOException;

/**
 * @author Liby
 * @date 2022-04-26 10:28
 * @description:建立WebSocket连接
 * @version:
 */
@RestController
@RequestMapping("/webSocket")
public class WebSocketController {
    @Autowired
    private WebSocket webSocket;
    @PostMapping("/sentMessage")
    public void sentMessage(String userId,String message){
        try {
            webSocket.sendMessageByUserId(userId,message);
        } catch (IOException e) {
            e.printStackTrace();
        }

    }

}

5.webSocket网页客户端工具
http://websocket.jsonin.com/

在这里插入图片描述

复制三个tab

WebSocket地址分别输入后连接

ws://127.0.0.1:8092/websocket/1
ws://127.0.0.1.132:8092/websocket/2
ws://127.0.0.1.132:8092/websocket/3

在这里插入图片描述

6.通讯测试

1.往客户端发消息

127.0.0.1:8092/webSocket/sentMessage?userId=1&message=请进入视频会议
127.0.0.1:8092/webSocket/sentMessage?userId=2&message=请进入视频会议
127.0.0.1:8092/webSocket/sentMessage?userId=3&message=请进入视频会议

在这里插入图片描述

2.客户端收到消息回复“好的”

在这里插入图片描述

在这里插入图片描述

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/weixin_43757027/article/details/124454843

智能推荐

failed to load ldlinux.c32_vmware failed to load ldlinux.c32-程序员宅基地

文章浏览阅读1.5k次。转:https://blog.csdn.net/tnaig/article/details/81139887 https://www.cnblogs.com/huadongw/p/6516637.html 同时附上了评论,感谢精彩评论!!!Ubuntu安装时出现“failed to load ldlinux.c32”U盘启动总是..._vmware failed to load ldlinux.c32

linux问题一句话(程序篇)-程序员宅基地

文章浏览阅读1.3k次。----------------------------程序开发篇-------------------------- 0001 linux下调试core文件(bjchenxu) gdb :出错产生core dump的可执行程序。 : core dump的文件名,缺省是“core” 0002 gcc abc.c得到的a.out不能运行(bjchenxu) ./a.out 0003 c++ 编译

这个小孩绝对聪明~_考题火车行驶中遇到玩耍的孩童-程序员宅基地

文章浏览阅读849次。 _考题火车行驶中遇到玩耍的孩童

C++ 创建文件夹与子文件夹_::access 和 ::mkdir-程序员宅基地

文章浏览阅读2.2w次,点赞4次,收藏15次。C++中fopen函数是没有创建文件夹功能的,也就是说如果‍‍".\\1\\2\\3\\"这个目录不存在,那么下面的代码是运行报错的。char *fileName=".\\1\\2\\3\\a.txt";FILE *ftest=fopen(fileName,"w");fprintf(ftest,"test\naldf\naldkf\m\n");fclose(ftest);要预防_::access 和 ::mkdir

XGB 训练的时候添加自定义eval_metric:f1、准确率,并对样本、特征加权训练_xgb eval_metric-程序员宅基地

文章浏览阅读5k次,点赞7次,收藏23次。文章目录XGB 训练的时候添加自定义eval_metric:f1、准确率,并对样本、特征加权训练随机搜索XGB 训练的时候添加自定义eval_metric:f1、准确率,并对样本、特征加权训练以下demo重点说明:eval_metric:用以训练的时候评估,必须要指定验证集,本博文分享自定义准确率、f1verbose:训练的时候对验证集评估是否打印,True和1等价,比如verbose=10,就会打印n_estimators // 10次feature_weights:特征加权训练,给一个sof_xgb eval_metric

matlab fhog,fhog.m · wycjl/kcf_matlab_apce - Gitee.com-程序员宅基地

文章浏览阅读320次。function H = fhog( I, binSize, nOrients, clip, crop )% Efficiently compute Felzenszwalb's HOG (FHOG) features.%% A fast implementation of the HOG variant used by Felzenszwalb et al.% in their work on ..._fhog matlab

随便推点

Arduino开发板使用矩阵键盘的方法_arduino矩阵键盘输入-程序员宅基地

文章浏览阅读1.4w次,点赞5次,收藏34次。键盘允许用户在程序运行时输入数据。本篇文章主要介绍如何将一个带有十二个按键的键盘连接到Arduino开发板以及如何使用库Keypad.h。通常需要键盘来为Arduino开发板提供输入信号,而薄膜键盘是许多应用的经济型解决方案。它们非常薄,可以轻松安装在任何需要的地方。在本篇文章中,我们将演示如何使用12键数字键盘,类似于电话上的键盘。 12键键盘有三列四行。按下按钮会将其中一个行输出短接到..._arduino矩阵键盘输入

Ubuntu Anaconda tensorflow install_ubuntu anaconda3 tensorflow-cpu == 2.3.0-程序员宅基地

文章浏览阅读2.1k次。本文为Anaconda3-5.2.0 Python3.6.5的tensorflow1.12.0安装教程之前的python -m pip --upgradeThenpip install tensorflow-cpu==2.3.0 -i https://pypi.douban.com/simple/自动升级为新版本,没有意义解决方案sudo apt install python3-pip......_ubuntu anaconda3 tensorflow-cpu == 2.3.0

video自动播放_<video data-v-eef4c0fe="" controls="controls" auto-程序员宅基地

文章浏览阅读6.9k次。Documentcontrols="controls" 是控制按钮autoplay="autoplay" 是自动播放-->_

STM32兴趣篇四:STM32F103C8T6工控板与LabVIEW的串口通讯实例_labview控制stm32的运行-程序员宅基地

文章浏览阅读8.4k次,点赞15次,收藏141次。串口通信(Serial Communications)是指外设与计算机间,通过数据线按位进行传输数据的一种通讯方式。尽管比按字节(byte)的并行通信慢,但是串口可以在使用一根线发送数据的同时用另一根线接收数据。虽然串口通信传输速度不高,但是程序简单,能实现远距离通信且远距离通信成本较低,通信长度可达1200米。常用的仪器仪表大多都支持串口通信协议。LabVIEW的自带函数库中有现成的串口通信模块,方便快速搭建堪比串口调试助手的软件。今天分享一个STM32F103C8T6工控板与LabVIEW的串口通_labview控制stm32的运行

离职了,写点什么吧~_大队长离职该写点什么好-程序员宅基地

文章浏览阅读2.1w次,点赞43次,收藏15次。离职了,写点什么吧~_大队长离职该写点什么好

PCM音频文件_pcm测试文件下载-程序员宅基地

文章浏览阅读9.6k次。关于音频格式有好多种,如常见的mp3压缩格式的音频;PCM音频文件,就是我们从计算机中采集出来未压缩的声音数据;我们可以直接通过写文件的方式不断的将数据写到一个文件中,后缀为.pcm,这个文件就可以通过pcm播放器播放了。pcm没有文件头,全部就是采集的未压缩的音频数据。关于音频有自己的格式,在windows中用WAVEFORMATEX结构体表示。 PCM播放器:Music_pcm测试文件下载

推荐文章

热门文章

相关标签