SpringBoot+WebSocket

发布时间:2023年12月18日

SpringBoot+WebSocket

1.导入依赖:

-- Spring Boot 2.x 使用 javax.websocket

-- Spring Boot 3.x 使用 jakarta.websocket
		
		
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
    <exclusions>
        <exclusion>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-tomcat</artifactId>
        </exclusion>
    </exclusions>
</dependency>
		
package com.js.config;


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

/**
 * websocket配置类中
 */
@Configuration
public class WebSocketConfig {
    /**
     *  第一个配置是因为使用springboot内置容器,自己开发时需要配置,如果有独立的容器需要将其注释掉,
     * 也就意味着,如果将项目打成WAR包,部署到服务器,使用Tomcat启动时,需要注释掉ServerEndpointExporter配置;
     * @return
     */
    @Bean
    public ServerEndpointExporter serverEndpointExporter() {
        return new ServerEndpointExporter();
    }

    /**
     * MyEndpointConfigure配置是因为我的需求需要,需要在websocket类中注入service层或者dao层的接口,
     * MyEndpointConfigure配置就是为了解决websocket无法注入的问题,如果没有需要可以不用配置
     * @return
     */
    @Bean
    public MyEndpointConfigure newConfigure() {
        return new MyEndpointConfigure();
    }
}

package com.js.httpclenlient;
import com.hy.core.toolkit.util.CollectionUtil;
import org.springframework.stereotype.Component;

import javax.annotation.Resource;
import javax.websocket.*;
import javax.websocket.server.PathParam;
import javax.websocket.server.ServerEndpoint;
import java.io.IOException;
import java.util.*;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.stream.Collectors;

/**
 * @author
 * @Description: WebSocket服务端代码,包含接收消息,推送消息等接口
 */
@Component
@ServerEndpoint(value = "/socket/{archivecode}")
public class WebSocketServer {

    //静态变量,用来记录当前在线连接数。应该把它设计成线程安全的。
    private static AtomicInteger online = new AtomicInteger();

    //concurrent包的线程安全Set,用来存放每个客户端对应的WebSocketServer对象的session。
    private static Map<Long, List<Session>> sessionPools = new HashMap<>();


    @Resource
    private RemoteAccessClient remoteAccessClient;

    /**
     * 发送消息方法
     * @param sessions 客户端与socket建立的会话
     * @param message 消息
     * @throws IOException
     */
    public void sendMessage(List<Session> sessions, String message) throws IOException{
        if(CollectionUtil.isNotEmpty(sessions)){
            sessions.stream().forEach(
                    session-> {
                        try {
                            session.getBasicRemote().sendText(message);
                        } catch (IOException e) {
                            throw new RuntimeException(e);
                        }
                    }
            );

        }
    }


    /**
     * 连接建立成功调用
     * @param session 客户端与socket建立的会话
     * @param archivecode 客户端的archivecode
     */
    @OnOpen
    public void onOpen(Session session, @PathParam(value = "archivecode") Long archivecode){
        List<Session> sessions = sessionPools.get(archivecode);
        if(CollectionUtil.isNotEmpty(sessions)){
            sessions.add(session);
            sessionPools.put(archivecode, sessions);
        }else {
            List<Session> sessionList = new ArrayList<>();
            sessionList.add(session);
            sessionPools.put(archivecode, sessionList);
        }

        addOnlineCount();
        System.out.println(archivecode + "加入webSocket!当前人数为" + online);
        try {
            sendMessage( sessions, "欢迎" + archivecode + "加入连接!");
        } catch (IOException e) {
            throw new IllegalArgumentException("webSocket建立连接失败");
        }
    }

    /**
     * 关闭连接时调用
     * @param 关闭连接的客户端的session
     */
    @OnClose
    public void onClose(Session session,@PathParam(value = "archivecode") String archivecode){

        Set<Long> sessionkey = sessionPools.keySet();
        for (Long key : sessionkey) {
            List<Session> sessions = sessionPools.get(key);
            boolean contains = sessions.contains(session);
            if(contains){
                sessions.remove(session);
            }

        }

        sessionPools.remove(archivecode);
        subOnlineCount();
        System.out.println(archivecode + "断开webSocket连接!当前人数为" + online);
    }

    /**
     * 收到客户端消息时触发(群发)
     * @param message
     * @throws IOException
     */

    @OnMessage
    public void onMessage(String message) throws IOException{
        Set<Long> companyIds = sessionPools.keySet();
        for (Long companyid : companyIds) {
            List<Session> sessions = sessionPools.get(companyid);
            try {
                sendMessage(sessions, message);
            } catch(Exception e){
                e.printStackTrace();

            }
        }

    }

    /**
     * 发生错误时候
     * @param session
     * @param throwable
     */
    @OnError
    public void onError(Session session, Throwable throwable){
        System.out.println("发生错误");
        throwable.printStackTrace();
    }

    /**
     * 给指定用户发送消息
     * @param archivecode 用户名
     * @param message 消息
     * @throws IOException
     */
    public void sendInfo(Long archivecode, String message){
        List<Session> sessions = sessionPools.get(archivecode);
        try {
            sendMessage(sessions, message);
        }catch (Exception e){
            e.printStackTrace();
        }
    }

    /**
     * 根据用户名获取session会话对象
     * @param archivecode
     * @return
     */
    public  List<Session> getSession(Long archivecode){
        List<Session> sessions = sessionPools.get(archivecode);
        return sessions;
    }

    public static void addOnlineCount(){
        online.incrementAndGet();
    }

    public static void subOnlineCount() {
        online.decrementAndGet();
    }


}

package com.js.controller;


import cn.hutool.json.JSONUtil;
import org.springframework.web.bind.annotation.*;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.List;

/**
     *  6.密集架报警信息实时更新获取
     */

    @RequestMapping(value = "/denseRack/alarm", method = RequestMethod.POST)
    public void testSocket1(@RequestBody FilealertInformation dto) throws JsonProcessingException {
        Long archiveCompanyId = ThreadLocalUtil.getArchiveCompanyId();
        dto.setPageRefreshType("warning");
        String s = JSONUtil.toJsonStr(dto);
        webSocketServer.sendInfo(archiveCompanyId, s);
    }

测试发送

在这里插入图片描述

结果

在这里插入图片描述

Spring boot接入websocket时,启动报错

Spring Boot 2.x

javax.websocket.server.ServerContainer not available

Spring Boot 3.x

jakarta.websocket.server.ServerContainer not available

问题原因

1. 因为spring boot内带tomcat,tomcat中的websocket会有冲突。
解决方案:

? spring boot启动时排除tomcat依赖包即可

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
    <exclusions>
        <exclusion>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-tomcat</artifactId>
        </exclusion>
    </exclusions>
</dependency>

@SpringBootTest启动时没有启动servlet

解决方案:

@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)

Spring Boot 与WebSocket API版本不对应

Spring Boot 2.x 使用 javax.websocket

Spring Boot 3.x 使用 jakarta.websocket

解决方案:

<!-- SpringBoot3 引入以下依赖,并删除之前的 javax.websocket依赖 -->
<dependency>
    <groupId>jakarta.websocket</groupId>
    <artifactId>jakarta.websocket-api</artifactId>
    <version>2.1.0</version>
</dependency>
文章来源:https://blog.csdn.net/qq_45525848/article/details/135067592
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。