• Spring Boot中使用Websocket搭建即时聊天系统


    1、首先在pom文件中引入Webscoekt的依赖

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

    2、通过注解方式构建Websocket服务

    package com.kk.server.chat.websocket;
    
    import com.kk.server.chat.service.CheckTokenService;
    import com.kk.server.chat.service.QueryService;
    import com.kk.server.chat.service.SaveMongodbService;
    import org.apache.log4j.Logger;
    import org.springframework.context.ApplicationContext;
    import org.springframework.stereotype.Component;
    
    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;
    
    /**
     * Created by yxl on 2018-03-19.
     */
    @ServerEndpoint("/chat/{userId}") //关键注解,将WebSocket服务端运行在 ws://[Server端IP或域名]:[Server端口]/websockets/chat/{userId} 的访问端点,客户端浏览器已经可以对 WebSocket客户端API发起HTTP长连接了。
    @Component
    public class ChatWebsocket {
    
        private Logger logger = Logger.getLogger(ChatWebsocket.class);
    
        private static ApplicationContext applicationContext;
    
        private SaveMongodbService saveMongodbService;
    
        private CheckTokenService checkTokenService;
    
        private QueryService queryService;
    
    
    
        //静态变量,用来记录当前在线连接数。应该把它设计成线程安全的。
        private static int onlineCount = 0;
        //concurrent包的线程安全Set,用来存放每个客户端对应的MyWebSocket对象。若要实现服务端与单一客户端通信的话,可以使用Map来存放,其中Key可以为用户标识
        private static Map<String, Session> webSocketSet = new ConcurrentHashMap<>(); //医生web
    
    
        //解决Websocket不能注入bean的问题
        public static void setApplicationContext(ApplicationContext applicationContext) {
            ChatWebsocket.applicationContext = applicationContext;
        }
    
        /**
         * 连接建立成功调用的方法
         *
         * @param userId   用户链接ID
         */
        @OnOpen  //建立连接的注解,当用户建立连接时SpringBoot会监听到,然后会调用该注解下的方法
        public void onOpen(@PathParam("userId") String userId, Session session) {
            webSocketSet.put(userId, session);
        }
    
    
    
        /**
         * 连接关闭调用的方法
         */
        @OnClose
        public void onClose(Session session) {
            logger.info("进入关闭方法!");
            if (session != null) {
                Map<String, String> pathParameters = session.getPathParameters();
                String userId = pathParameters.get("userId"); //从当前关闭session中获取用户ID,前提是建立websocket连接时带有userId,即 @ServerEndpoint("/chat/{userId}")
                webSocketSet.remove(userId); //从map中移除用户
            }
        }
    
    
    
        /**
         * 收到客户端消息后调用的方法
         *
         * @param message 客户端发送过来的消息
         * @param session 可选的参数
         */
        @OnMessage
        public void onMessage(String message, Session session) throws IOException {
    
            try {
                JSONObject jsonObject = JSONObject.parseObject(message);
                String receiver_id = jsonObject.getString("receiver_id") //接受者ID,通过该Id来获取接受者的session
                Session session = webSocketSet.get("receiver_id")
                if (session != null) {
                    session.getBasicRemote.sendText(message); //通过session发送消息
                }else {
                    //TODO 接收方不在线,处理离线消息
    
                }
            }catch(IOException e) {
                e.printStackTrace;
            }
    
        }
    
        /**
         * 发生错误时调用
         *
         * @param session
         * @param error
         */
        @OnError
        public void onError(Session session, Throwable error) {
            //onClose();
            if (session != null) {
                Map<String, String> pathParameters = session.getPathParameters();
                String userId = pathParameters.get("userId"); //从当前关闭session中获取用户ID,前提是建立websocket连接时带有userId,即 @ServerEndpoint("/chat/{userId}")
                webSocketSet.remove(userId); //从map中移除用户
            }
            logger.info("webscoket发生错误!"+error.getMessage());
            error.printStackTrace();
        }
    }

    3、前台调用方法(js)

    function WebSocketTest()
             {
                if ("WebSocket" in window)
                {
                   alert("您的浏览器支持 WebSocket!");
                   
                   // 打开一个 web socket
                var ws = new WebSocket("ws://服务器IP:端口/chat/123");
                   ws.onopen = function() {
                      // Web Socket 已连接上,使用 send() 方法发送数据
                      ws.send('{"userId":"1555855","message" : "test"}');
                      ws.close("666666");
                      alert("数据发送中...");
                   };
               
                   ws.onmessage = function (evt) { 
                      console.log(evt)
                      alert(evt.data);
                      alert("数据已接收...");
                   };
                    
                   ws.onclose = function(){ 
                      // 关闭 websocket
                      alert("连接已关闭..."); 
                   };
                }else {
                   // 浏览器不支持 WebSocket
                   alert("您的浏览器不支持 WebSocket!");
                }
             }        

     4、当然,在使用websocket聊天时,可能会遇到一些数据需要查询数据库或者Redis,但是在websocket中又不能直接注入相应的Bean实例,这个时候可以看我的另一篇博客https://www.cnblogs.com/Amaris-Lin/p/9038813.html

  • 相关阅读:
    listen and translation exercise 52
    中译英20
    listen and translation exercise 51
    中译英19
    listen and translation exercise 49
    PyQt(Python+Qt)学习随笔:QTableView的showGrid属性
    PyQt(Python+Qt)学习随笔:视图中类QAbstractItemView的dragDropOverwriteMode属性
    PyQt(Python+Qt)学习随笔:视图中的dragDropMode属性对dragEnabled和acceptDrops属性的影响
    PyQt(Python+Qt)学习随笔:QAbstractItemView的dragEnabled和dragDropMode属性的关系
    PyQt(Python+Qt)学习随笔:QAbstractItemView的verticalScrollMode和horizontalScrollMode属性
  • 原文地址:https://www.cnblogs.com/Amaris-Lin/p/9982200.html
Copyright © 2020-2023  润新知