SpringBoot集成WebSocket

SpringBoot集成WebSocket

项目结构图

项目架构图

前端项目

socket.js 注意前端这里的端口是9000, 路劲是ws开头

js 复制代码
function createScoket(token){
    var socket;
    if(typeof(WebSocket) == "undefined") {
        console.log("您的浏览器不支持WebSocket");
    }else{
        var host = window.location.origin.replace("http:","ws:")
        //实现化WebSocket对象,指定要连接的服务器地址与端口  建立连接
        socket = new WebSocket(host+":9000/ws/"+token);
        //打开事件
        socket.onopen = function() {
            console.log("Socket 已打开");
            //socket.send("这是来自客户端的消息" + location.href + new Date());
        };
        //获得消息事件
        socket.onmessage = function(result) {
            console.log("客户端收到服务端发送的消息: " + result);
            console.log("result.data: " + result.data);
        };
        //关闭事件
        socket.onclose = function() {
            console.log("Socket已关闭");
        };
        //发生了错误事件
        socket.onerror = function() {
            console.log("Socket发生了错误");
        }
        //窗口关闭
        $(window).unload(function(event){
            socket.close();
        });
    }
    return socket;
}

index1.html 创建socket唯一标识就是, 后端会把它当做key

html 复制代码
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>客户端A</title>
    <script src="jquery.js"></script>
    <script src="socket.js"></script>
    <script>
        var socket;
        function connect() {
            if (!socket) {
                // 建立socket链接的唯一标识
                socket = createScoket("123");
            }

        }

        function sendMessage() {
            var content = $("#content").val();
            socket.send(content);
        }
    </script>
</head>
<body>
<input type="button" value="链接服务器" onclick="connect()" /> <br>
<input type="text" id="content"> <input type="button" value="发送消息" onclick="sendMessage()" />
</body>
</html>

index2.html

html 复制代码
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>客户端B</title>
    <script src="jquery.js"></script>
    <script src="socket.js"></script>
    <script>
        var socket;
        function connect() {
            if (!socket) {
                // 建立socket链接的唯一标识
                socket = createScoket("234");
            }

        }

        function sendMessage() {
            var content = $("#content").val();
            socket.send(content);
        }
    </script>
</head>
<body>
<input type="button" value="链接服务器" onclick="connect()" /> <br>
<input type="text" id="content"> <input type="button" value="发送消息" onclick="sendMessage()" />
</body>
</html>

后端项目

  1. 需要引入websocket的依赖

    xml 复制代码
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-websocket</artifactId>
    </dependency>
  2. WebSocketServer.class 这里编写websocket根据前端做长连接的接口地址

    • @OnOpen 建立socket链接
    • @OnMessage 接收客户端发送的消息
    • @OnClose 关闭socket链接
    • @OnError socket异常信息
    java 复制代码
    package com.xiaoge;
    
    import org.springframework.stereotype.Component;
    
    import javax.websocket.*;
    import javax.websocket.server.PathParam;
    import javax.websocket.server.ServerEndpoint;
    import java.util.concurrent.ConcurrentHashMap;
    
    /**
     * todo 类里面这几个方法注解事件, 对应着前端相同的事件
     */
    @ServerEndpoint("/ws/{token}")  // 这个注解相当于RequestMapping, 写路径, 这个路径跟前端约定好的, 可以看前端项目的socket.js文件里面的createScoket方法
    @Component
    public class WebSocketServer {
        private Session session;
        public static ConcurrentHashMap<String,Session> clients = new ConcurrentHashMap<>();
    
        /**
         * 建立socket链接
         * @param session
         * @param token
         */
        @OnOpen
        public void onOpen(Session session, @PathParam( "token") String token){
            // 建立和浏览器的会话映射关系
            System.out.println("浏览器和服务器建立连接");
            clients.put(token,session);
        }
    
        /**
         * 接收客户端发送的消息
         * @param msg
         */
        @OnMessage
        public void onMessage(@PathParam( "token") String token, String msg) {
            System.out.println("收到客户端标识为: "+ token +"发送的消息: " + msg);
        }
    
        /**
         * 关闭socket链接
         * @param token
         */
        @OnClose
        public void onClose(@PathParam( "token") String token){
            System.out.println("浏览器和服务器断开连接");
            clients.remove(token);
        }
    
        /**
         * socket报错
         *
         * @param error
         */
        @OnError
        public void onError(Throwable error) {
            error.printStackTrace();
        }
    }
  3. 启动类型上, 把ServerEndpointExporter 注入到IOC , 就会扫描我们贴了**@ServerEndpoint**注解的类, 把它的地址发布出去

    java 复制代码
    package com.xiaoge;
    
    import org.springframework.boot.SpringApplication;
    import org.springframework.boot.autoconfigure.SpringBootApplication;
    import org.springframework.context.annotation.Bean;
    import org.springframework.web.socket.server.standard.ServerEndpointExporter;
    
    /**
     * TODO
     *
     * @author <a href="mailto:1330137071@qq.com">Zhang Xiao</a>
     * @since
     */
    @SpringBootApplication
    public class WebsocketServerApplication {
        public static void main(String[] args) {
            SpringApplication.run(WebsocketServerApplication.class, args);
        }
    
    
        /**
         * 扫描我们贴了@ServerEndpoint注解的类, 把它的地址发布出去
         * @return
         */
        @Bean
        public ServerEndpointExporter serverEndpointExporter() {
            return new ServerEndpointExporter();
        }
    }
  4. MessageController.class 通过这个**/sendMsg**接口对跟socket长连接的前端通信

    java 复制代码
    package com.xiaoge;
    
    import org.springframework.web.bind.annotation.GetMapping;
    import org.springframework.web.bind.annotation.RestController;
    
    import javax.websocket.Session;
    import java.io.IOException;
    
    /**
     * TODO
     *
     * @author <a href="mailto:1330137071@qq.com">Zhang Xiao</a>
     * @since
     */
    @RestController
    public class MessageController {
    
    
        /**
         * todo 访问这个接口的时候, websocket就会给跟websocket建立连接的客户变标识为token的发送消息
         * @param token
         * @param msg
         * @return
         * @throws IOException
         */
        @GetMapping("/sendMsg")
        public String sendMessage(String token, String msg) throws IOException {
            Session session = WebSocketServer.clients.get(token);
            // 正常应该需要判断session是否为空, 但是我这里就不判断了
            session.getBasicRemote().sendText(msg);
            return "消息发送成功!";
        }
    
    }

演示

客户端A/B链接后端socket

客户端A/B给socket发送消息

后端给前端发送消息

应对很多的Socket链接应该怎么应对?

客户端通过nginx, 使用ip_hash到对应的webSocket服务, 应用程序通过mq广播模式发送到每一台webSocket服务, 总有一台是保存了, 对应的用户长连接的, 它会把消息通知给前端。

客户端不是关闭浏览器, 那样socket就没有被删除, 它只是断网了, 你不知道它还需不需要这个socket, 长此以往下去, 容易内存溢出, 这时候就需要心跳机制, 每隔一段时间发送一次心跳, 更新心跳时间, 这样就知道那些socket有用, 那些socket没用, 这样就可以把对应的socket链接删除。

demo地址: https://download.csdn.net/download/zsx1314lovezyf/88269081?spm=1001.2014.3001.5503

相关推荐
摇滚侠4 分钟前
Spring Boot 3零基础教程,整合Redis,笔记12
spring boot·redis·笔记
荣淘淘4 分钟前
互联网大厂Java求职面试全景实战解析(涵盖Spring Boot、微服务及云原生技术)
java·spring boot·redis·jwt·cloud native·microservices·interview
吃饭最爱14 分钟前
spring高级知识概览
spring boot
舒克日记34 分钟前
基于springboot针对老年人的景区订票系统
java·spring boot·后端
沐雨橙风ιε1 小时前
Spring Boot整合Apache Shiro权限认证框架(实战篇)
java·spring boot·后端·apache shiro
一线大码2 小时前
开发 Java 项目时的命名规范
java·spring boot·后端
本就一无所有 何惧重新开始2 小时前
Redis技术应用
java·数据库·spring boot·redis·后端·缓存
低音钢琴2 小时前
【SpringBoot从初学者到专家的成长11】Spring Boot中的application.properties与application.yml详解
java·spring boot·后端
不太会写3 小时前
又开始了 小程序定制
vue.js·spring boot·python·小程序