zoukankan      html  css  js  c++  java
  • SpringBoot之集成WebSocket

    websocket是什么不做介绍。开发环境:jdk1.8,win7_64旗舰版,idea
     
    1、初始化一个springboot项目
     
    2、加入websocket依赖
    <!-- springboot的websocket依赖 -->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-websocket</artifactId>
    </dependency>

    pom.xml如下:

    <dependencies>
            <!-- 模板引擎 -->
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-thymeleaf</artifactId>
            </dependency>
            <!-- web -->
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-web</artifactId>
            </dependency>
            <!-- websocket -->
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-websocket</artifactId>
            </dependency>
            <!---->
            <dependency>
                <groupId>org.mybatis.spring.boot</groupId>
                <artifactId>mybatis-spring-boot-starter</artifactId>
                <version>2.0.0</version>
            </dependency>
    
            <!-- lombok工具 -->
            <dependency>
                <groupId>org.projectlombok</groupId>
                <artifactId>lombok</artifactId>
                <optional>true</optional>
            </dependency>
            <!-- 内置tomcat -->
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-tomcat</artifactId>
                <scope>provided</scope>
            </dependency>
            <!-- 测试 -->
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-test</artifactId>
                <scope>test</scope>
            </dependency>
    
        </dependencies>

    3、编写websocket的服务端

        3.1、WebSocketEndPoint是websocket服务端的核心
          @PathParam是javax.websocket.server下的注解,是将路径中绑定的占位符的值取出来
      在url中使用key和name,是想通过key和name对websocket的连接进行访问控制,这个key可以是用户登录后服务器给用户的令牌,通过令牌和和name进行权限验证(自己写拦截器或者继承权限框架实现),还可以通过key和name生成唯一值来进行在线websocket
    连接的维护<(key+name), websocketSession>, 当然,我在这里没有这样做。
    package com.geniuses.sewage_zero_straight.net.websocket;
    
    import com.geniuses.sewage_zero_straight.service.UserService;
    import lombok.extern.slf4j.Slf4j;
    import org.springframework.beans.factory.annotation.Autowired;
    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 static com.geniuses.sewage_zero_straight.net.websocket.WebSocketPool.*;
    import static com.geniuses.sewage_zero_straight.net.websocket.WebSocketHandler.createKey;
    
    @Slf4j
    @Component
    @ServerEndpoint("/net/websocket/{key}/{name}")//表明这是一个websocket服务的端点
    public class WebSocketEndPoint {
    
    
        private static UserService userService;
    
        @Autowired
        public void setUserService(UserService userService){
            WebSocketEndPoint.userService = userService;
        }
    
        @OnOpen
        public void onOpen(@PathParam("key") String key, @PathParam("name") String name,  Session session){
            log.info("有新的连接:{}", session);
            add(createKey(key, name), session);
            WebSocketHandler.sendMessage(session, key + name);
            log.info("在线人数:{}",count());
            sessionMap().keySet().forEach(item -> log.info("在线用户:", item));
            for (Map.Entry<String, Session> item : sessionMap().entrySet()){
                log.info("12: {}", item.getKey());
            }
        }
    
        @OnMessage
        public void onMessage(String message){
            log.info("有新消息: {}", message);
        }
    
        @OnClose
        public void onClose(@PathParam("key") String key, @PathParam("name") String name,Session session){
            log.info("连接关闭: {}", session);
            remove(createKey(key, name));
            log.info("在线人数:{}",count());
            sessionMap().keySet().forEach(item -> log.info("在线用户:", (item.split("@"))[1]));
            for (Map.Entry<String, Session> item : sessionMap().entrySet()){
                log.info("12: {}", item.getKey());
            }
        }
    
        @OnError
        public void onError(Session session, Throwable throwable){
            try {
                session.close();
            } catch (IOException e) {
                log.error("onError Exception: {}", e);
            }
            log.info("连接出现异常: {}", throwable);
        }
    
    }

     

      3.2、WebSocketPool是websocket的在线连接池

    package com.geniuses.sewage_zero_straight.net.websocket;
    
    import lombok.extern.slf4j.Slf4j;
    
    import javax.websocket.Session;
    import java.util.Map;
    import java.util.concurrent.ConcurrentHashMap;
    
    @Slf4j
    public class WebSocketPool {
    
        //在线用户websocket连接池
        private static final Map<String, Session> ONLINE_USER_SESSIONS = new ConcurrentHashMap<>();
    
        /**
         * 新增一则连接
         * @param key
         * @param session
         */
        public static void add(String key, Session session){
            if (!key.isEmpty() && session != null){
                ONLINE_USER_SESSIONS.put(key, session);
            }
        }
    
        /**
         * 根据Key删除连接
         * @param key
         */
        public static void remove(String key){
            if (!key.isEmpty()){
                ONLINE_USER_SESSIONS.remove(key);
            }
        }
    
        /**
         * 获取在线人数
         * @return
         */
        public static int count(){
            return ONLINE_USER_SESSIONS.size();
        }
    
        /**
         * 获取在线session池
         * @return
         */
        public static Map<String, Session> sessionMap(){
            return ONLINE_USER_SESSIONS;
        }
    }
     3.3、WebSocketHandler是websocket的动作处理工具
    package com.geniuses.sewage_zero_straight.net.websocket;
    
    import lombok.extern.slf4j.Slf4j;
    
    import javax.websocket.RemoteEndpoint;
    import javax.websocket.Session;
    import java.io.IOException;
    import static com.geniuses.sewage_zero_straight.net.websocket.WebSocketPool.sessionMap;
    
    @Slf4j
    public class WebSocketHandler {
    
    
        /**
         * 根据key和用户名生成一个key值,简单实现下
         * @param key
         * @param name
         * @return
         */
        public static String createKey(String key, String name){
            return key + "@" + name;
        }
    
        /**
         * 给指定用户发送信息
         * @param session
         * @param msg
         */
        public static void sendMessage(Session session, String msg){
            if (session == null)
                return;
            final RemoteEndpoint.Basic basic = session.getBasicRemote();
            if (basic == null)
                return;
            try {
                basic.sendText(msg);
            } catch (IOException e) {
                log.error("sendText Exception: {}", e);
            }
        }
    
    
        /**
         * 给所有的在线用户发送消息
         * @param message
         */
        public static void sendMessageAll(String message){
            log.info("广播:群发消息");
            sessionMap().forEach((key, session) -> sendMessage(session, message));
        }
    }

    4、前端访问实现

        4.1、index.html,页面引用了jquery和bootstrap样式,请自行应用

    <!DOCTYPE html>
    <html lang="en" xmlns:th="http://www.springframework.org/schema/mvc">
    <head>
        <meta charset="UTF-8">
        <title>chat room websocket</title>
        <link rel="stylesheet" th:href="@{/css/bootstrap.min.css}">
        <script th:src="@{/js/jquery-3.3.1.min.js}" ></script>
    </head>
        <body class="container" style=" 60%">
            <div class="form-group" ></br>
                <h5>聊天室</h5>
                <textarea id="message_content"  class="form-control"  readonly="readonly" cols="50" rows="10"></textarea>
            </div>
            <div class="form-group" >
                <label for="in_user_name">用户姓名 &nbsp;</label>
                <input id="in_user_name" value="" class="form-control" /></br>
                <button id="user_join" class="btn btn-success" >加入聊天室</button>
                <button id="user_exit" class="btn btn-warning" >离开聊天室</button>
            </div>
            <div class="form-group" >
                <label for="in_room_msg" >群发消息 &nbsp;</label>
                <input id="in_room_msg" value="" class="form-control" /></br>
                <button id="user_send_all" class="btn btn-info" >发送消息</button>
            </div>
        </body>
        <<script type="text/javascript">
            $(document).ready(function(){
                var urlPrefix ='ws://192.168.2.156:8080/net/websocket/12/';
                var ws = null;
                $('#user_join').click(function(){
                    var username = $('#in_user_name').val();
                    var url = urlPrefix + username;
                    ws = new WebSocket(url);
                    ws.onopen = function () {
                        console.log("建立 websocket 连接...");
                    };
                    ws.onmessage = function(event){
                        //服务端发送的消息
                        $('#message_content').append(event.data+'
    ');
                    };
                    ws.onclose = function(){
                        $('#message_content').append('用户['+username+'] 已经离开聊天室!' + '
    ');
                        console.log("关闭 websocket 连接...");
                    }
                });
                //客户端发送消息到服务器
                $('#user_send_all').click(function(){
                    var msg = $('#in_room_msg').val();
                    if(ws){
                        ws.send(msg);
                    }
                });
                // 退出聊天室
                $('#user_exit').click(function(){
                    if(ws){
                        ws.close();
                    }
                });
            })
        </script>
    </html>

        

      4.2、页面访问控制器,由此来访问index.html

    package com.geniuses.sewage_zero_straight.web;
    
    import org.springframework.stereotype.Controller;
    import org.springframework.web.bind.annotation.GetMapping;
    import org.springframework.web.bind.annotation.RequestMapping;
    
    @RequestMapping("/view")
    @Controller
    public class ViewController {
    
        /**
         * 返回首页
         * @return
         */
        @GetMapping("/index")
        public String index(){
            return "index";
        }
    }

    5、websocket配置

    package com.geniuses.sewage_zero_straight.config;
    
    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;
    
    @Configuration
    @EnableWebSocket
    public class WebSocketConfig {
    
        @Bean
        public ServerEndpointExporter serverEndpointExporter(){
            return new ServerEndpointExporter();
        }
    }
     
    6、注意:
        在使用了@ServerEndpoint注解的类是无法直接使用@Autowired的,因为@ServerEndpoint表明当前类是websocket的服务端点,在spring容器启动时会初始化一次该类,当有新的websocket连接的时候,也会进行该类实例的创建(每一次连接时都会创建一个实例),所以在第二次往后创建该类实例的时候,就无法进行有效的@Autowired了,此时发现,即便第一次注入是有效的,但是也没有什么用。这个时候,将需要注入的变量置为类的变量,提供一个set方法(该方法为实例方法),在set方法上面进行依赖注入,这样就可以进行有效的注入了。
     
    7、这里只是websocket的简单实现,更多情况...
  • 相关阅读:
    初学java-基础
    初学java-基础
    HYPER-V 实现管理器远程管理虚拟机
    zeromq rpc原型
    haproxy+keepalived
    [转]序列化悍将Protobuf-Net,入门动手实录
    RabbitMQ的几种应用场景
    redis数据类型及使用场景
    [转]DDD领域驱动设计基本理论知识总结
    Centos 下编译安装Redis
  • 原文地址:https://www.cnblogs.com/threadj/p/10552904.html
Copyright © 2011-2022 走看看