• Spring / Boot整合 WebSocket-入门Demo


    WebSocket是基于TCP的一种新的网络协议。它实现了浏览器与服务器全双工(full-duplex)通信—与我们常用HTTP最大的不同是,他允许服务器主动发送信息给客户端。

    SpringBoot整合支持WebSocket
    • 依赖
    <!--webSocket 依赖-->
    <dependency>
        <groupId>org.springframework.boot</groupId>
         <artifactId>spring-boot-starter-websocket</artifactId>
    </dependency>
    
    • 开启webSocket支持
    @Configuration
    public class WebSocketConfig {
        @Bean
        public ServerEndpointExporter serverEndpointExporter() {
            return new ServerEndpointExporter();
        }
    }
    
    • 服务端Server配置
    @Component
    @ServerEndpoint("/websocket/{ip}")
    public class WebSocketServer {
        private static Logger logger = LoggerFactory.getLogger(WebSocketServer.class);
    
        /**静态变量,用来记录当前在线连接数。应该把它设计成线程安全的。*/
        private static int onlineCount = 0;
        /**concurrent包的线程安全Set,用来存放每个客户端对应的MyWebSocket对象。*/
        private static ConcurrentHashMap<String,WebSocketServer> webSocketMap = new ConcurrentHashMap<>();
        /**与某个客户端的连接会话,需要通过它来给客户端发送数据*/
        private Session session;
        /**接收ip*/
        private String ip="";
    
        /**
         * 连接建立成功调用的方法*/
        @OnOpen
        public void onOpen(Session session,@PathParam("ip") String ip) {
            this.session = session;
            this.ip=ip;
            if(webSocketMap.containsKey(ip)){
                webSocketMap.remove(ip);
                webSocketMap.put(ip,this);
                //加入set中
            }else{
                webSocketMap.put(ip,this);
                //加入set中
                addOnlineCount();
                //在线数加1
            }
    
            logger.info("用户连接:"+ip+",当前在线人数为:" + getOnlineCount());
    
            try {
                sendMessage("连接成功");
            } catch (IOException e) {
                logger.error("用户:"+ip+",网络异常!!!!!!");
            }
        }
        /**
         * 连接关闭调用的方法
         */
        @OnClose
        public void onClose() {
            if(webSocketMap.containsKey(ip)){
                webSocketMap.remove(ip);
                //从set中删除
                subOnlineCount();
            }
            logger.info("用户退出:"+ip+",当前在线人数为:" + getOnlineCount());
        }
    
        /**
         * 收到客户端消息后调用的方法
         *
         * @param message 客户端发送过来的消息*/
        @OnMessage
        public void onMessage(String message, Session session) {
            logger.info("用户IP:"+ip+",报文:"+message);
            //可以群发消息
            //消息保存到数据库、redis
            if(!StringUtils.isEmpty(message)){
                try {
                    //解析发送的报文
                    JSONObject jsonObject = JSON.parseObject(message);
                    //追加发送人(防止串改)
                    jsonObject.put("fromIP",this.ip);
                    String toUserIP=jsonObject.getString("ip");
                    //传送给对应toIP用户的websocket
                    if(!StringUtils.isEmpty(toUserIP)&&webSocketMap.containsKey(toUserIP)){
                        webSocketMap.get(toUserIP).sendMessage(jsonObject.toJSONString());
                    }else{
                        logger.error("请求的IP:"+toUserIP+"不在该服务器上");
                        //否则不在这个服务器上,发送到mysql或者redis
                    }
                }catch (Exception e){
                    e.printStackTrace();
                }
            }
        }
    
        /**
         *
         * @param session
         * @param error
         */
        @OnError
        public void onError(Session session, Throwable error) {
            logger.error("用户错误:"+this.ip+",原因:"+error.getMessage());
            error.printStackTrace();
        }
        /**
         * 实现服务器主动推送
         */
        public void sendMessage(String message) throws IOException {
            this.session.getBasicRemote().sendText(message);
        }
    
    
        /**
         * 发送自定义消息
         * */
        public static void sendInfo(String message,@PathParam("ip") String ip) throws IOException {
            logger.info("发送消息到:"+ip+",报文:"+message);
            if(!StringUtils.isEmpty(ip)&&webSocketMap.containsKey(ip)){
                webSocketMap.get(ip).sendMessage(message);
            }else{
                logger.error("用户"+ip+",不在线!");
            }
        }
    
        public static synchronized int getOnlineCount() {
            return onlineCount;
        }
    
        public static synchronized void addOnlineCount() {
            WebSocketServer.onlineCount++;
        }
    
        public static synchronized void subOnlineCount() {
            WebSocketServer.onlineCount--;
        }
    
    }
    
    • 在Controller层写一个测试接口,主要测试往浏览器(客户端)发送消息
     @RequestMapping("/push/{toUserIP}")
        public ResponseEntity<String> pushToWeb(String message, @PathVariable String toUserIP) throws IOException {
            WebSocketServer.sendInfo("发送测试消息............",toUserIP);
            return ResponseEntity.ok("MSG SEND SUCCESS");
        }
    

    前端HTML-Demo

    打开后进入调试模式,服务端给该页面发送的消息,在控制台可以显示

    <!DOCTYPE html>
    <html>
    <head>
        <meta charset="utf-8">
        <title>websocket通讯</title>
    </head>
    <script src="https://cdn.bootcss.com/jquery/3.3.1/jquery.js"></script>
    <script>
        var socket;
        function openSocket() {
            if(typeof(WebSocket) == "undefined") {
                console.log("您的浏览器不支持WebSocket");
            }else{
                console.log("您的浏览器支持WebSocket");
                //实现化WebSocket对象,指定要连接的服务器地址与端口  建立连接
                //等同于socket = new WebSocket("ws://localhost:8888/xxxx/im/25");
                //var socketUrl="${request.contextPath}/im/"+$("#userId").val();
                var socketUrl="http://localhost:8000/api/websocket/"+$("#ip").val();
                socketUrl=socketUrl.replace("https","ws").replace("http","ws");
                console.log(socketUrl);
                if(socket!=null){
                    socket.close();
                    socket=null;
                }
                socket = new WebSocket(socketUrl);
                //打开事件
                socket.onopen = function() {
                    console.log("websocket已打开");
                    //socket.send("这是来自客户端的消息" + location.href + new Date());
                };
                //获得消息事件
                socket.onmessage = function(msg) {
                    console.log(msg.data);
                    //发现消息进入    开始处理前端触发逻辑
                };
                //关闭事件
                socket.onclose = function() {
                    console.log("websocket已关闭");
                };
                //发生了错误事件
                socket.onerror = function() {
                    console.log("websocket发生了错误");
                }
            }
        }
        function sendMessage() {
            if(typeof(WebSocket) == "undefined") {
                console.log("您的浏览器不支持WebSocket");
            }else {
                console.log("您的浏览器支持WebSocket");
                console.log('{"toUserId":"'+$("#toUserId").val()+'","contentText":"'+$("#contentText").val()+'"}');
                socket.send('{"toUserId":"'+$("#toUserId").val()+'","contentText":"'+$("#contentText").val()+'"}');
            }
        }
    </script>
    <body>
    <p>【userId】:<div><input id="ip" name="ip" type="text" value="192.168.1.1"></div>
    <p>【toUserId】:<div><input id="toUserId" name="toUserId" type="text" value="192.168.1.100"></div>
    <p>【toUserId】:<div><input id="contentText" name="contentText" type="text" value="hello websocket"></div>
    <p>【操作】:<div><a onclick="openSocket()">开启socket</a></div>
    <p>【操作】:<div><a onclick="sendMessage()">发送消息</a></div>
    </body>
    
    </html>
    

    效果图


    相关资料:

    阮一峰的网络日志--- WebSocket 教程

  • 相关阅读:
    Makefile:2:*** missing separator. Stop.
    Code笔记之:对使用zend加密后的php文件进行解密
    Apache 访问控制
    leetcode-21-合并两个有序链表
    tcp四次挥手的过程
    实现一个LRU算法
    redis为什么快
    二月春日
    你的支持会鼓励我更积极地创作
    静夜思·静夜祈愿
  • 原文地址:https://www.cnblogs.com/YangGC/p/12222035.html
Copyright © 2020-2023  润新知