springboot配置websocket

springbootboot配置websocket

java 复制代码
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;

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


@Component
@Slf4j
@ServerEndpoint("/websocketHandler/{userId}")
public class MyWebSocketHandler {

    /**
     * 线程安全Map
     */
    private static ConcurrentHashMap<String, Session> sessionPool = new ConcurrentHashMap<>();

    /**
     * Redis触发监听名字
     */
    public static final String REDIS_TOPIC_NAME = "socketHandlerReport";


    //==========【websocket接受、推送消息等方法 ------ 具体服务节点推送ws消息】========================================================================================
    @OnOpen
    public void onOpen(Session session, @PathParam(value = "userId") String userId) {
        try {
            sessionPool.put(userId, session);
            log.info("【系统 WebSocket】有新的连接,总数为:" + sessionPool.size());
        } catch (Exception e) {
        }
    }

    @OnClose
    public void onClose(@PathParam("userId") String userId) {
        try {
            sessionPool.remove(userId);
            log.info("【系统 WebSocket】连接断开,总数为:" + sessionPool.size());
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /**
     * ws推送消息
     *
     * @param userId
     * @param message
     */
    public void pushMessage(String userId, String message) {
        for (Map.Entry<String, Session> item : sessionPool.entrySet()) {
            //userId key值= {用户id + "_"+ 登录token的md5串}

            if (item.getKey().contains(userId)) {
                Session session = item.getValue();
                try {
                    //update-begin-author:taoyan date:20211012 for: websocket报错 https://gitee.com/jeecg/jeecg-boot/issues/I4C0MU
                    synchronized (session) {
                        log.info("【系统 WebSocket】推送单人消息:" + message);
                        session.getBasicRemote().sendText(message);
                    }
                    //update-end-author:taoyan date:20211012 for: websocket报错 https://gitee.com/jeecg/jeecg-boot/issues/I4C0MU
                } catch (Exception e) {
                    log.error(e.getMessage(), e);
                }
            }
        }
    }

    /**
     * ws遍历群发消息
     */
    public void pushMessage(String message) {
        try {
            for (Map.Entry<String, Session> item : sessionPool.entrySet()) {
                try {
                    item.getValue().getAsyncRemote().sendText(message);
                } catch (Exception e) {
                    log.error(e.getMessage(), e);
                }
            }
            log.info("【系统 WebSocket】群发消息:" + message);
        } catch (Exception e) {
            log.error(e.getMessage(), e);
        }
    }


    /**
     * ws接受客户端消息
     */
    @OnMessage
    public void onMessage(String message, @PathParam(value = "userId") String userId) {
        if (!"ping".equals(message) && !WebsocketConst.CMD_TOPIC.equals(message)) {
            log.info("【系统 WebSocket】收到客户端消息:" + message);
        } else {
            log.debug("【系统 WebSocket】收到客户端消息:" + message);
        }

        //------------------------------------------------------------------------------
//        JSONObject obj = new JSONObject();
//        //业务类型
//        obj.put(WebsocketConst.MSG_CMD, WebsocketConst.CMD_CHECK);
//        //消息内容
//        obj.put(WebsocketConst.MSG_TXT, "心跳响应");
//        this.pushMessage(userId, obj.toJSONString());
        //------------------------------------------------------------------------------

        //--------PC端消息推送--------
        Session session = sessionPool.get(userId);
        if (session != null && session.isOpen()) {
            try {
                log.info("【websocket消息】 单点消息:" + message);
                session.getAsyncRemote().sendText(message);
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
        //--------增加APP端消息推送--------
        Session session_app = sessionPool.get(userId + APP_SESSION_SUFFIX);
        if (session_app != null && session_app.isOpen()) {
            try {
                log.info("【websocket移动端消息】 单点消息:" + message);
                session_app.getAsyncRemote().sendText(message);
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }

    /**
     * 配置错误信息处理
     *
     * @param session
     * @param t
     */
    @OnError
    public void onError(Session session, Throwable t) {
        log.warn("【系统 WebSocket】消息出现错误");
        //t.printStackTrace();
    }
    //==========【系统 WebSocket接受、推送消息等方法 ------ 具体服务节点推送ws消息】========================================================================================


    //==========【采用redis发布订阅模式------推送消息】========================================================================================

    /**
     * 后台发送消息到redis
     *
     * @param message
     */
    public void sendMessage(String message) {
        //log.info("【系统 WebSocket】广播消息:" + message);
//        BaseMap baseMap = new BaseMap();
//        baseMap.put("userId", "");
//        baseMap.put("message", message);
//        jeecgRedisClient.sendMessage(REDIS_TOPIC_NAME, baseMap);
    }

    /**
     * 此为单点消息 redis
     *
     * @param userId
     * @param message
     */
    public void sendMessage(String userId, String message) {
//        BaseMap baseMap = new BaseMap();
//        baseMap.put("userId", userId);
//        baseMap.put("message", message);
//        jeecgRedisClient.sendMessage(REDIS_TOPIC_NAME, baseMap);
    }

    /**
     * 此为单点消息(多人) redis
     *
     * @param userIds
     * @param message
     */
    public void sendMessage(String[] userIds, String message) {
        for (String userId : userIds) {
            sendMessage(userId, message);
        }
    }
    //=======【采用redis发布订阅模式------推送消息】==========================================================================================


    //1.增加app端标识
    private String APP_SESSION_SUFFIX = "_app";

    public void sendOneMessage(String userId, String message) {
        //--------PC端消息推送--------
        Session session = sessionPool.get(userId);
        if (session != null && session.isOpen()) {
            try {
                log.info("【websocket消息】 单点消息:" + message);
                session.getAsyncRemote().sendText(message);
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
        //--------增加APP端消息推送--------
        Session session_app = sessionPool.get(userId + APP_SESSION_SUFFIX);
        if (session_app != null && session_app.isOpen()) {
            try {
                log.info("【websocket移动端消息】 单点消息:" + message);
                session_app.getAsyncRemote().sendText(message);
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }
}
java 复制代码
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.socket.config.annotation.EnableWebSocket;
import org.springframework.web.socket.server.standard.ServerEndpointExporter;

/**
 * 开启WebSocket支持
 */
@Configuration
@EnableWebSocket
public class WebSocketConfig {
    @Bean
    public ServerEndpointExporter serverEndpointExporter() {
        return new ServerEndpointExporter();
    }
}
java 复制代码
public class WebsocketConst {


    /**
     * 消息json key:cmd
     */
    public static final String MSG_CMD = "cmd";

    /**
     * 消息json key:msgId
     */
    public static final String MSG_ID = "msgId";

    /**
     * 消息json key:msgTxt
     */
    public static final String MSG_TXT = "msgTxt";

    /**
     * 消息json key:userId
     */
    public static final String MSG_USER_ID = "userId";

    /**
     * 消息json key:chat
     */
    public static final String MSG_CHAT = "chat";

    /**
     * 消息类型 heartcheck
     */
    public static final String CMD_CHECK = "heartcheck";

    /**
     * 消息类型 user 用户消息
     */
    public static final String CMD_USER = "user";

    /**
     * 消息类型 topic 系统通知
     */
    public static final String CMD_TOPIC = "topic";

    /**
     * 消息类型 email
     */
    public static final String CMD_EMAIL = "email";

    /**
     * 消息类型 meetingsign 会议签到
     */
    public static final String CMD_SIGN = "sign";

    /**
     * 消息类型 新闻发布/取消
     */
    public static final String NEWS_PUBLISH = "publish";

}

项目启动后遇到如下错误:

java 复制代码
Invocation of init method failed; nested exception is java.lang.IllegalStateException: javax.websocket.server.ServerContainer not available

用IDEA启动项目,所报的异常,打jar包服务器上运行则不报,原因是spring boot内带tomcat,tomcat中的websocket会有冲突出现问题。我的解决办法:

注释掉WebSocketConfig中的

java 复制代码
    @Bean
    public ServerEndpointExporter serverEndpointExporter() {
        return new ServerEndpointExporter();
    }
相关推荐
长栎1 分钟前
你的策略模式是 Map<String, Strategy>?那不过是最廉价的 if-else 替代品
后端
长栎8 分钟前
你写的 abstract class 里全是钩子方法——模板模式不是让你填空,是让你别越界
后端
ping某11 分钟前
语法树,到底是一棵什么形状的树?
后端
_柳青杨16 分钟前
一文吃透 Node.js 事件循环:从原理到 Node 20+ 重大变更
javascript·后端
Alson_Code33 分钟前
人机协作项目文档--HITL-AgentScope
后端·aigc·ai编程
IT_陈寒43 分钟前
Java 并行流把我坑惨了,这6小时加班值了
前端·人工智能·后端
葫芦和十三1 小时前
图解 MongoDB 03|CRUD 全链路:一条 find 怎么穿过 WiredTiger
后端·mongodb·agent
葫芦和十三9 小时前
图解 MongoDB 04|索引模型:每建一个索引,就是在 B+-tree 森林里多栽一棵
后端·mongodb·agent
用户479492835691511 小时前
claude Fable用不了?把Gpt 5.5pro接到你的claude code里
前端·后端
GetcharZp13 小时前
告别 Nginx 复杂配置!这款带 Web 面板的万能代理神器,让端口转发变得如此简单
后端