SpringBoot整合websocket,解决启动报错问题

SpringBoot整合websocket踩的坑

最近做一个项目需要为前端大屏推送信息,使用的是SpringBoot,需在项目中整合websocket实时推送信息,且只用有连接的时候再推送。

第一步:在SpringBoot中整合进入websocket

先是pom.xml添加依赖

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

无需再配置文件中添加任何配置,可直接使用项目端口。

第二步:websocket配置类

添加websocket配置类 WebSocketConfig.java

/**
 * @Component 
 * @since jdk1.8
 */
@Component
public class WebSocketConfig {

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

第三步:各节点监听发放类

MonitorWebSocket.java

/**
 * 
 * 此处在本地启动和Linux服务器部署启动会有冲突,最后详细说
 *
 */
@Component
@ServerEndpoint(value = "/websocket/monitor/{userId}")
public class MonitorWebSocket  {

    private static final Logger logger = LoggerFactory.getLogger(MonitorWebSocket.class);
    /**静态变量,用来记录当前在线连接数。应该把它设计成线程安全的。*/
    private static int onlineCount = 0;
    /**concurrent包的线程安全Set,用来存放每个客户端对应的MyWebSocket对象。*/
    private static ConcurrentHashMap<String, MonitorWebSocket> webSocketMap = new ConcurrentHashMap<>();
    /**与某个客户端的连接会话,需要通过它来给客户端发送数据*/
    private Session session;
    /**接收userId*/
    private String userId="";

    /**
     * 连接建立成功调用的方法*/
    @OnOpen
    public void onOpen(Session session, @PathParam("userId") String userId) {
        this.session = session;
        this.userId=userId;
        if(webSocketMap.containsKey(userId)){
            webSocketMap.remove(userId);
            webSocketMap.put(userId,this);
            //加入set中
        }else{
            webSocketMap.put(userId,this);
            //加入set中
            addOnlineCount();
            //在线数加1
        }

        logger.info("/websocket/monitor/用户连接:"+userId+",当前在线人数为:" + getOnlineCount());

        try {
            sendMessage(JSONObject.toJSONString(R.ok("连接成功")));
        } catch (IOException e) {
            logger.error("用户:{},网络异常!!!!!!:{}",userId,e.getMessage());
        }
    }

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

    /**
     * 收到客户端消息后调用的方法
     *
     * @param message 客户端发送过来的消息*/
    @OnMessage
    public void onMessage(String message, Session session) {
        System.out.println("用户消息:"+userId+",报文:"+message);
        //可以群发消息
        //消息保存到数据库、redis
        if(StringUtils.isNotBlank(message)){
            try {
                //解析发送的报文
                JSONObject jsonObject = JSONObject.parseObject(message);
                //追加发送人(防止串改)
                jsonObject.put("fromUserId",this.userId);
                String toUserId=jsonObject.getString("toUserId");
                //传送给对应toUserId用户的websocket
                if(StringUtils.isNotBlank(toUserId)&&webSocketMap.containsKey(toUserId)){
                    webSocketMap.get(toUserId).sendMessage(jsonObject.toJSONString());
                }else{
                    logger.info("请求的userId:"+toUserId+"不在该服务器上");
                    //否则不在这个服务器上,发送到mysql或者redis
                }
            }catch (Exception e){
                logger.error(e.getMessage());
            }
        }
    }

    /**
     * session会话错误
     * @param session
     * @param error
     */
    @OnError
    public void onError(Session session, Throwable error) {
        logger.error("用户错误:{},原因:{}", this.userId, error.getMessage());
    }

    /**
     * 实现服务器主动推送
     */
    public void sendMessage(String message) throws IOException {
        this.session.getBasicRemote().sendText(message);
    }


    /**
     * 发送自定义消息
     * */
    public static void sendInfo(String message,@PathParam("userId") String userId) throws IOException {
        logger.info("发送消息到:{},报文:{}", userId, message);
        if(StringUtils.isNotBlank(userId)&&webSocketMap.containsKey(userId)){
            webSocketMap.get(userId).sendMessage(message);
        }else{
            logger.error("用户{},不在线!", userId);
        }
    }


    /**
     * 给所有用户连接推送消息
     * @author cd
     * @date 2020/3/10
     * @param message
     * @return
     */
    public static void sendMessageAll(String message) throws IOException {

        for (MonitorWebSocket item : webSocketMap.values()) {
            item.session.getAsyncRemote().sendText(message);
        }

    }

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

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

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

}

第四步:前端页面写法

// 建立websocket连接,进入页面后台推送数据
var websocket = null;
var host = document.location.host;
// 获得一个随机数作为连接的唯一标识, 此处是封装的一个获取指定位数的随机数方法
var username = RndNum(10); 
//判断当前浏览器是否支持WebSocket
if (websocket != null) {
    websocket.close();
    websocket = null;
}
if ('WebSocket' in window) {
    websocket = new WebSocket('ws://' + host + '/websocket/monitor/' + username);
} else {
    alert('当前浏览器 Not support websocket')
}
//连接成功建立的回调方法
websocket.onopen = function () {
    console.log("websocket通道连接成功!")
}
//连接发生错误的回调方法
websocket.onerror = function () {
    console.log("websocket连接发生错误!")
};

//接收到消息的回调方法
websocket.onmessage = function (event) {
    setMessageInnerHTML(event.data);
}
//连接关闭的回调方法
websocket.onclose = function () {
    console.log("websocket连接关闭!")
}
//监听窗口关闭事件,当窗口关闭时,主动去关闭websocket连接,防止连接还没断开就关闭窗口,server端会抛异常。
window.onbeforeunload = function () {
    websocket.close();
}
//将消息显示在网页上
function setMessageInnerHTML(msg) {
    
    // document.getElementById('message').innerHTML += innerHTML + '<br/>';

}

以下是踩的坑
按照上面的写法程序在本地启动、测试,不会有任何问题,但是当把项目部署到Linux服务器的tomcat 中时启动会报错

java.lang.IllegalStateException: Failed to register @ServerEndpoint class:

为了解决这个问题本人在网上查找了大量的资料,基本的解决办法就是说


MonitorWebSocket类中不需要在@ServerEndpoint之前使用@Component注解,去掉后就可以在服务器正常启动
还有一种是说添加jar包的,这种本人也试过,服务器确实可以正常启动

以上两种解决方法都可以解决服务器启动报错问题,但是这种写法在本地启动项目的时候前端无法建立连接,还是不能推送数据,至今未找到如何使本地和服务器同时满足不需要修改代码的解决方案,最后只能在部署到服务器时将MonitorWebSocket.java类中的@Component注解去掉,若哪位大神有更好的解决办法留言私信我,谢谢

#修改方法(已测试)
将项目中引入的webSocket的jar包修改为

<dependency>
  	<groupId>org.springframework</groupId>
   <artifactId>spring-websocket</artifactId>
</dependency>
  • 8
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 5
    评论
要实现Spring Boot整合WebSocket,你需要进行以下步骤: 1. 首先,在pom.xml文件中添加WebSocket的相关依赖。可以使用以下两个依赖之一: - 从中提到的依赖: ```xml <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-websocket</artifactId> </dependency> ``` - 从中提到的依赖: ```xml <!--webSocket--> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-websocket</artifactId> </dependency> ``` 2. 创建WebSocket配置类,这个类负责配置WebSocket的相关信息。你可以按照以下方式创建一个配置类[3]: ```java package com.loit.park.common.websocket; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.web.socket.server.standard.ServerEndpointExporter; @Configuration public class WebSocketConfig { @Bean public ServerEndpointExporter serverEndpointExporter() { return new ServerEndpointExporter(); } } ``` 3. 至此,你已经完成了WebSocket整合配置。现在,你可以在你的应用中创建WebSocket的控制器并定义你的WebSocket端点。你可以根据你的需求来实现WebSocket端点的业务逻辑。 这就是Spring Boot整合WebSocket的基本步骤。通过这种方式,你可以在Spring Boot应用中轻松地使用WebSocket进行实通信。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* [springboot整合websocket](https://blog.csdn.net/weixin_45390688/article/details/120448778)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 50%"] - *2* *3* [springboot整合websocket(详解、教程、代码)](https://blog.csdn.net/hjq_ku/article/details/127503180)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 50%"] [ .reference_list ]
评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

码农踩坑之旅

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值