首页 > 其他分享 >spring-websocket 简单使用

spring-websocket 简单使用

时间:2023-09-13 19:44:25浏览次数:40  
标签:websocket sendId spring 简单 springframework import org roomId public

  之前自己基于netty 实现了websocket 协议,实现单聊以及群聊。这里记录下spring 封装的 spring-websocket 使用方式。

1. 后端

1. pom

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
    <parent>
        <artifactId>cloud</artifactId>
        <groupId>cn.qz.cloud</groupId>
        <version>1.0-SNAPSHOT</version>
    </parent>
    <modelVersion>4.0.0</modelVersion>
    <artifactId>cloud-websocket</artifactId>
    <name>Archetype - cloud-websocket</name>
    <url>http://maven.apache.org</url>

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-actuator</artifactId>
        </dependency>
        <!--基础配置-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-devtools</artifactId>
            <scope>runtime</scope>
            <optional>true</optional>
        </dependency>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <optional>true</optional>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-websocket</artifactId>
            <version>5.3.9</version>
        </dependency>
        <dependency>
            <groupId>cn.hutool</groupId>
            <artifactId>hutool-all</artifactId>
            <version>5.8.3</version>
        </dependency>
    </dependencies>
</project>

2. yml

server:
  port: 8802

3. 相关类

vo:
package cn.qz.vo;

import lombok.Data;

@Data
public class MessageRequestVO {

    /**
     * 业务消息类型
     */
    private int msgType;

    /**
     * 发送者userId
     */
    private Long sendUserId;

    /**
     * 业务类型
     */
    private int bizType;

    /**
     * 业务模块
     */
    private int bizOptModule;

    /**
     * 接收者userId
     */
    private Long receivedUserId;

    /**
     * 消息
     */
    private String msg;
}


WebSocketOneToOneController:
package cn.qz.web;

import cn.hutool.json.JSONUtil;
import cn.qz.vo.MessageRequestVO;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.RestController;

import javax.websocket.*;
import javax.websocket.server.PathParam;
import javax.websocket.server.ServerEndpoint;
import java.io.IOException;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

/**
 * @description 聊天
 * <p>
 * 描述: 该对象是多例
 * 一对一聊天
 * @ServerEndpoint 注解是一个类层次的注解,它的功能主要是将目前的类定义成一个websocket服务器端,
 * 注解的值将被用于监听用户连接的终端访问URL地址,客户端可以通过这个URL来连接到WebSocket服务器端
 */
@RestController
@ServerEndpoint(value = "/webSocketOneToOne/{sendId}/{roomId}")
@Slf4j
public class WebSocketOneToOneController {

    // 静态变量,用来记录当前在线连接数。应该把它设计成线程安全的。
    private static int onlineCount;
    //实现服务端与单一客户端通信的话,可以使用Map来存放,其中Key为用户标识
    private static final Map<Long, WebSocketOneToOneController> connections = new ConcurrentHashMap<>();
    // 与某个客户端的连接会话,需要通过它来给客户端发送数据
    private Session session;
    private Long sendId;
    private String roomId;



    /**
     * 连接建立成功调用的方法
     *
     * @param session 可选的参数。session为与某个客户端的连接会话,需要通过它来给客户端发送数据
     */
    @OnOpen
    public void onOpen(@PathParam("sendId") Long sendId, @PathParam("roomId") String roomId, Session session) {
        this.session = session;
        this.sendId = sendId;             //用户标识
        this.roomId = roomId;         //会话标识
        connections.put(sendId, this);     //添加到map中
        addOnlineCount();               // 在线数加
        log.info("sendId:" + sendId + "roomId:" + roomId);
        System.out.println(this.session);
        System.out.println("有新连接加入!新用户:" + sendId + ",当前在线人数为" + getOnlineCount());
    }

    /**
     * 连接关闭调用的方法
     */
    @OnClose
    public void onClose() {
        connections.remove(sendId);  // 从map中移除
        subOnlineCount();          // 在线数减
        System.out.println("有一连接关闭!当前在线人数为" + getOnlineCount());
    }

    /**
     * 收到客户端消息后调用的方法
     *
     * @param message 客户端发送过来的消息
     * @param session 可选的参数
     */
    @OnMessage
    public void onMessage(String message, Session session) {
        // 打印消息
        System.out.println("来自客户端的消息:" + message);
        if ("ping".equals(message)) {
            return;
        }

        // 将消息落库等操作

        // 发送消息
        try {
            MessageRequestVO messageRequestVO = JSONUtil.toBean(message, MessageRequestVO.class);
            //如果消息接收者在线,发给消息接受者
            if (messageRequestVO != null && messageRequestVO.getReceivedUserId() != null) {
                WebSocketOneToOneController con = connections.get(messageRequestVO.getReceivedUserId());
                if (con != null) {
                    if (roomId.equals(con.roomId)) {
                        con.session.getBasicRemote().sendText(message);
                    }
                }
            }
            //通知发送消息的,消息已经发送成功
            WebSocketOneToOneController confrom = connections.get(sendId);
            if (confrom != null) {
                if (roomId.equals(confrom.roomId)) {
                    confrom.session.getBasicRemote().sendText("ok");
                }
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    /**
     * 发生错误时调用
     *
     * @param session
     * @param error
     */
    @OnError
    public void one rror(Session session, Throwable error) {
        System.out.println("发生错误");
        error.printStackTrace();
    }


    /**
     * @param msg             消息内容
     * @param sendId          发送人
     * @param receiveId       接收人
     * @param roomId          房间ID
     * @param msgType         消息类型
     * @param requestId       消息请求ID
     * @param lastMessageTime 最后一次的消息时间
     * @param giftId          礼物ID
     */
    public void send(String msg, Long sendId, Long receiveId, String roomId, int msgType, String requestId, String lastMessageTime, Long giftId) {
        try {
            //通知发送消息的狗逼,消息已经发送成功
            WebSocketOneToOneController confrom = connections.get(sendId);

        } catch (Exception e) {
            e.printStackTrace();
        }
    }

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

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

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

}



WebSocketStompConfig 配置类:

package cn.qz.config;

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

/**
 * @description websocket 配置
 **/
@Configuration
public class WebSocketStompConfig {

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



Springboot 主启动类:
package cn.qz;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;

@SpringBootApplication
@RequestMapping("/")
public class MainApplication {

    public static void main(String[] args) {
        SpringApplication.run(MainApplication.class, args);
    }

    @GetMapping()
    @ResponseBody
    public String index() {
        return "index";
    }
}
  

2. 前端测试HTML

<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/html">
  <head>
    <title>WebSocket Example</title>
  </head>
  <body>
    登录用户ID:<input type="text" id="sendUserId" /></br>
    接受用户ID:<input type="text" id="receivedUserId" /></br>
    发送消息内容:<input type="text" id="messageInput" /></br>
    接受消息内容:<input type="text" id="messageReceive" /></br>
    <button onclick="sendMessage()">Send</button>

    <script>
	  // 随机发送者
	  var sendUserId = Math.floor(Math.random() * 1000000);
	  // 房间号码。 真实的房间号码可以是 消息发送方和接收方组成的房间。 动态生成的。
	  var roomId = 123
      var socket = new WebSocket(`ws://localhost:8802/webSocketOneToOne/${sendUserId}/${roomId}`);

      document.getElementById("sendUserId").value = sendUserId;
      socket.onopen = function (event) {
        console.log("WebSocket is open now.");
        let loginInfo = {
          msgType: 2, //登录消息
          sendUserId: sendUserId,
          bizType: 1, //业务类型
          bizOptModule: 1, //业务模块
          roomId: roomId,
          msg: 'login'
        };
        socket.send(JSON.stringify(loginInfo));
      };

	  var messageReceive = document.getElementById("messageReceive");
      socket.onmessage = function (event) {
        var message = event.data;
        console.log("Received message: " + message);
        messageReceive.value = message;
      };

      socket.onclose = function (event) {
        console.log("WebSocket is closed now.");
      };

      function sendMessage() {
        var message = document.getElementById("messageInput").value;
        var receivedUserId = document.getElementById("receivedUserId").value;
        let operateInfo = {
          msgType: 100, //业务消息
          sendUserId: sendUserId,
          bizType: 1, //业务类型
          bizOptModule: 1, //业务模块
          roomId: roomId,
          receivedUserId: receivedUserId,
          msg: message		
        };
        socket.send(JSON.stringify(operateInfo));
      }

      // 发送ping 消息
      setInterval(() => {
        socket.send("ping");
      }, 30000);
    </script>
  </body>
</html>

3. 测试

  1. 开启两个浏览器, 会生成两个userId

  2. 一个浏览器 发送消息给另一个userId

标签:websocket,sendId,spring,简单,springframework,import,org,roomId,public
From: https://www.cnblogs.com/qlqwjy/p/17700563.html

相关文章

  • springboot集成CAS客户端实现单点登录
    pom中引入依赖<!--cas--><dependency><groupId>org.jasig.cas.client</groupId><artifactId>cas-client-core</artifactId><version>3.6.2</version></dependen......
  • Spring MVC 教程,快速入门,深入分析
    SpringMVC教程,快速入门,深入分析资源下载:Spring_MVC_教程_快速入门_深入分析V1.1.pdfSpringMVC核心配置文件示例.rar 作者:赵磊博客:http://elf8848.iteye.com 目录一、前言二、springmvc核心类与接口三、springmvc 核心流程图四、springmvc DispatcherServlet......
  • Spring容器跳过指定对象初始化扫
    在Spring容器中,如果你希望跳过指定对象的初始化扫描,你可以使用`@ComponentScan`注解的`excludeFilters`属性来实现。`excludeFilters`属性可以指定扫描时需要排除的类或接口。例如,假设你有一个名为`MyBean`的对象,你不希望它被Spring容器初始化扫描。你可以创建一个自定义的过滤器来......
  • Spring高手之路14——深入浅出:SPI机制在JDK与Spring Boot中的应用
    1.SPI解读:什么是SPI?  SPI (ServiceProviderInterface)是一种服务发现机制,它允许第三方提供者为核心库或主框架提供实现或扩展。这种设计允许核心库/框架在不修改自身代码的情况下,通过第三方实现来增强功能。JDK原生的SPI:定义和发现:JDK的SPI主要通过在META-INF/services/目......
  • springboot 整合 nacos 实现配置文件统一管理 和 服务注册
    1.我使用的是 nacos-server-1.4.1 直接启动nacos没有配置数据库那些.\startup.cmd-mstandalone2.启动后在nacos中创建命名空间配置文件 注意命名空间的id我是自己定义的还有group到时候配置错了服务又不会报错只是会读取不到3.springboot配置 server-addr......
  • Springboot RocketMQ整合—官方原版
    Doker 技术人自己的数码品牌Doker官网:Doker多克一、添加maven依赖:<dependency><groupId>org.apache.rocketmq</groupId><artifactId>rocketmq-spring-boot-starter</artifactId><version>${RELEASE.VERSION}</version></dependen......
  • Dubbo配置问题____spring中dubbo标签报错问题和解决方案
    //1.当在springxml中引用dubbo标签时会报错windows-->preferrence-->xml->xmlcatalog-->add->catalogentry-->filesystem,选择模版文件后,修改key值为“http://code.alibabatech.com/schema/dubbo/dubbo.xsd”//资源文件下载 http://pan.baidu.com/s/1c2h6uzA......
  • Spring小技巧--计算表达式的值
    平时工作中经常要用到表达式值的计算问题,Spring框架中提供了SpringExpressionLanguage(简称SpEL)机制,可以很方便快捷的实现表达式值的计算;SpEL机制需要引入Spring-expression包。下面列举其应用的两个小Demo;1、数值计算:StringexpressionStr="19+26";ExpressionParse......
  • 对SpringBoot接口进行操作日志记录
    最近业务有需求要对所有的用户操作进行日志记录,方便管理员查询不同权限级别的用户对系统的操作记录,现有的日志只是记录了异常信息、业务出错、重要功能的执行进行了记录,并不能满足需求要求,最直接的解决方法是在每个接口上去添加log.info之类的代码,但是这种方式对业务代码的切入性......
  • flask 简单设置日志文件配置
    最近做了几个模型,需要配置接口提供使用,这时候就用到了日志系统首先创建一个logs.py文件,在文件中配置日志等级、保存路径、日志文件大小、日志输出格式importosimportloggingfromlogging.handlersimportRotatingFileHandler#获取当前绝对路径defget_cwd():r......