zoukankan      html  css  js  c++  java
  • Springboot:SpringBoot2.0整合WebSocket,实现后端数据实时推送!

    一、什么是WebSocket?

    B/S结构的软件项目中有时客户端需要实时的获得服务器消息,但默认HTTP协议只支持请求响应模式,这样做可以简化Web服务器,减少服务器的负担,加快响应速度,因为服务器不需要与客户端长时间建立一个通信链接,但不容易直接完成实时的消息推送功能,如聊天室、后台信息提示、实时更新数据等功能,但通过polling、Long polling、长连接、Flash Socket以及HTML5中定义的WebSocket能完成该功能需要。

    WebSocket是HTML5开始提供的一种在单个TCP连接上进行全双工通讯的协议,能更好的节省服务器资源和带宽,并且能够更实时地进行通讯。WebSocket 使得客户端和服务器之间的数据交换变得更加简单,允许服务端主动向客户端推送数据,在WebSocket API中,浏览器和服务器只需要完成一次握手,两者之间就直接可以创建持久性的连接,并进行双向数据传输。

    Socket又称"套接字",应用程序通常通过"套接字"向网络发出请求或者应答网络请求。Socket的英文原义是“孔”或“插座”,作为UNIX的进程通信机制。Socket可以实现应用程序间网络通信。

    Socket可以使用TCP/IP协议或UDP协议。

    TCP/IP协议

    TCP/IP协议是目前应用最为广泛的协议,是构成Internet国际互联网协议的最为基础的协议,由TCP和IP协议组成:
    TCP协议:面向连接的、可靠的、基于字节流的传输层通信协议,负责数据的可靠性传输的问题。

    IP协议:用于报文交换网络的一种面向数据的协议,主要负责给每台网络设备一个网络地址,保证数据传输到正确的目的地。

    UDP协议

    UDP特点:无连接、不可靠、基于报文的传输层协议,优点是发送后不用管,速度比TCP快。

    二、SpringBoot整合WebSocket

    新建一个spring boot项目spring-boot-websocket,按照下面步骤操作。

    1.pom.xml引入jar包

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

    2.新建WebSocket的配置类

    这个配置类检测带注解@ServerEndpoint的bean并注册它们,配置类代码如下:

    @Configuration
    public class WebSocketConfig {
        /**
         * 给spring容器注入这个ServerEndpointExporter对象
         * 相当于xml:
         * <beans>
         * <bean id="serverEndpointExporter" class="org.springframework.web.socket.server.standard.ServerEndpointExporter"/>
         * </beans>
         * <p>
         * 检测所有带有@serverEndpoint注解的bean并注册他们。
         *
         * @return
         */
        @Bean
        public ServerEndpointExporter serverEndpointExporter() {
            System.out.println("我被注入了");
            return new ServerEndpointExporter();
        }
    }

    3.新建WebSocket的处理类

    这个处理类需要使用@ServerEndpoint,这个类里监听连接的建立关闭、消息的接收等,具体代码如下:

    @ServerEndpoint(value = "/ws/asset")
    @Component
    public class WebSocketServer {
    
        @PostConstruct
        public void init() {
            System.out.println("websocket 加载");
        }
        private static Logger log = LoggerFactory.getLogger(WebSocketServer.class);
        private static final AtomicInteger OnlineCount = new AtomicInteger(0);
        // concurrent包的线程安全Set,用来存放每个客户端对应的Session对象。
        private static CopyOnWriteArraySet<Session> SessionSet = new CopyOnWriteArraySet<Session>();
    
    
        /**
         * 连接建立成功调用的方法
         */
        @OnOpen
        public void onOpen(Session session) {
            SessionSet.add(session);
            int cnt = OnlineCount.incrementAndGet(); // 在线数加1
            log.info("有连接加入,当前连接数为:{}", cnt);
            SendMessage(session, "连接成功");
        }
    
        /**
         * 连接关闭调用的方法
         */
        @OnClose
        public void onClose(Session session) {
            SessionSet.remove(session);
            int cnt = OnlineCount.decrementAndGet();
            log.info("有连接关闭,当前连接数为:{}", cnt);
        }
    
        /**
         * 收到客户端消息后调用的方法
         *
         * @param message
         *            客户端发送过来的消息
         */
        @OnMessage
        public void onMessage(String message, Session session) {
            log.info("来自客户端的消息:{}",message);
            SendMessage(session, "收到消息,消息内容:"+message);
    
        }
    
        /**
         * 出现错误
         * @param session
         * @param error
         */
        @OnError
        public void onError(Session session, Throwable error) {
            log.error("发生错误:{},Session ID: {}",error.getMessage(),session.getId());
            error.printStackTrace();
        }
    
        /**
         * 发送消息,实践表明,每次浏览器刷新,session会发生变化。
         * @param session
         * @param message
         */
        public static void SendMessage(Session session, String message) {
            try {
    //            session.getBasicRemote().sendText(String.format("%s (From Server,Session ID=%s)",message,session.getId()));
                session.getBasicRemote().sendText(message);
            } catch (IOException e) {
                log.error("发送消息出错:{}", e.getMessage());
                e.printStackTrace();
            }
        }
    
        /**
         * 群发消息
         * @param message
         * @throws IOException
         */
        public static void BroadCastInfo(String message) throws IOException {
            for (Session session : SessionSet) {
                if(session.isOpen()){
                    SendMessage(session, message);
                }
            }
        }
    
        /**
         * 指定Session发送消息
         * @param sessionId
         * @param message
         * @throws IOException
         */
        public static void SendMessage(String message,String sessionId) throws IOException {
            Session session = null;
            for (Session s : SessionSet) {
                if(s.getId().equals(sessionId)){
                    session = s;
                    break;
                }
            }
            if(session!=null){
                SendMessage(session, message);
            }
            else{
                log.warn("没有找到你指定ID的会话:{}",sessionId);
            }
        }
    }

    4.Html编写方式

    目前大部分浏览器支持WebSocket,比如Chrome, Mozilla,Opera和Safari,在html页面进行websocket的连接建立、收消息的监听,页面代码如下:

    <html>
    <head>
        <meta charset="UTF-8">
        <title>websocket测试</title>
        <script src="http://libs.baidu.com/jquery/2.0.0/jquery.min.js"></script>
        <style type="text/css">
            h3,h4{
                text-align:center;
            }
        </style>
    </head>
    <body>
    
    <h3>WebSocket测试,客户端接收到的消息如下:</h3>
    
    <textarea id = "messageId" readonly="readonly" cols="150" rows="30" >
    
    </textarea>
    
    
    <script type="text/javascript">
        var socket;
        if (typeof (WebSocket) == "undefined") {
            console.log("遗憾:您的浏览器不支持WebSocket");
        } else {
            console.log("恭喜:您的浏览器支持WebSocket");
            //实现化WebSocket对象
            //指定要连接的服务器地址与端口建立连接
            //注意ws、wss使用不同的端口。我使用自签名的证书测试,
            //无法使用wss,浏览器打开WebSocket时报错
            //ws对应http、wss对应https。
            socket = new WebSocket("ws://localhost:8080/ws/asset");
            //连接打开事件
            socket.onopen = function() {
                console.log("Socket 已打开");
                socket.send("消息发送测试(From Client)");
            };
            //收到消息事件
            socket.onmessage = function(msg) {
                $("#messageId").append(msg.data+ "
    ");
                console.log(msg.data  );
            };
            //连接关闭事件
            socket.onclose = function() {
                console.log("Socket已关闭");
            };
            //发生了错误事件
            socket.onerror = function() {
                alert("Socket发生了错误");
            }
            //窗口关闭时,关闭连接
            window.unload=function() {
                socket.close();
            };
        }
    </script>
    
    </body>
    </html>

    三、业务调用

    WebSocket ws = new WebSocket();
    JSONObject jo = new JSONObject();
    jo.put("message", "这个比密码不对还想登录!");
    jo.put("To", "admin");// 给用户名为admin的用户推送
    try {
        ws.onMessage(jo.toString());
    } catch (IOException e) {
        e.printStackTrace();
    }

    四、 查看运行效果

    启动SpringBoot项目

    1.打开首页

    本地浏览器打开首页http://localhost:8080/,出现WebSocket测试页面,同时后台打印连接的日志。

    有连接加入,当前连接数为:1,sessionId=0

    2.往客户端发送消息

    通过上面日志可以看到客户端连接连接的sessionId,我测试时候sessionId是0,然后浏览器访问下面接口即可往客户端发送消息。

    //参数说明: id:sessionID 
    //参数说明: message:消息内容
    http://localhost:8080/api/ws/sendOne?id=0&message=你好Java碎碎念

    到此SpringBoot整合WebSocket的功能已经全部实现。

    完整源码地址: https://github.com/suisui2019/springboot-study

    文章转载至:https://www.cnblogs.com/haha12/p/11933310.html

    ----------------------------------- 作者:怒吼的萝卜 链接:http://www.cnblogs.com/nhdlb/ -----------------------------------
  • 相关阅读:
    Redis学习篇(一)之String类型及其操作
    MySQL笔记(五)之表的连接
    MySQL笔记(三)之数据插入更新与删除
    MySQL笔记(四)之内建函数
    MySQL笔记(二)之数据检索常用关键字
    MySQL笔记(一)之新建数据库和数据表
    京东文胸数据分析
    用SpringSecurity从零搭建pc项目-02
    Spring Security构建Rest服务-0800-Spring Security图片验证码
    用SpringSecurity从零搭建pc项目-01
  • 原文地址:https://www.cnblogs.com/nhdlb/p/14289051.html
Copyright © 2011-2022 走看看