• WebSocket简单的应用


    WebSocket介绍

      WebSocket 是 HTML5 开始提供的一种在单个 TCP 连接上进行全双工通讯的协议。

      WebSocket 使得客户端和服务器之间的数据交换变得更加简单,允许服务端主动向客户端推送数据。在 WebSocket API 中,浏览器和服务器只需要完成一次握手,两者之间就直接可以创建持久性的连接,并进行双向数据传输。

      在 WebSocket API 中,浏览器和服务器只需要做一个握手的动作,然后,浏览器和服务器之间就形成了一条快速通道。两者之间就直接可以数据互相传送。

      现在,很多网站为了实现推送技术,所用的技术都是 Ajax 轮询。轮询是在特定的的时间间隔(如每1秒),由浏览器对服务器发出HTTP请求,然后由服务器返回最新的数据给客户端的浏览器。这种传统的模式带来很明显的缺点,即浏览器需要不断的向服务器发出请求,然而HTTP请求可能包含较长的头部,其中真正有效的数据可能只是很小的一部分,显然这样会浪费很多的带宽等资源。

      HTML5 定义的 WebSocket 协议,能更好的节省服务器资源和带宽,并且能够更实时地进行通讯。

    浏览器通过 JavaScript 向服务器发出建立 WebSocket 连接的请求,连接建立以后,客户端和服务器端就可以通过 TCP 连接直接交换数据。

      当你获取 Web Socket 连接后,你可以通过 send() 方法来向服务器发送数据,并通过 onmessage 事件来接收服务器返回的数据。

      以下 API 用于创建 WebSocket 对象。

      var Socket = new WebSocket(url, [protocol] );

      以上代码中的第一个参数 url, 指定连接的 URL。第二个参数 protocol 是可选的,指定了可接受的子协议。

    WebSocket 客户端页面实现

    <!DOCTYPE HTML>
    <html>
    <head>
    <meta charset="utf-8">
    <title>WebSocket测试</title>
    </head>
    <body style="text-align: center;">
        <h2>WebSocket测试</h2>
        <div>
            <input type="text" id="txt"/>
            <button onclick="sendWebSocket()">发送</button><br>
            <button onclick="checkWebSocket()">测试WebSocket</button>
            <button onclick="connectWebSocket()">连接WebSocket</button>
            <button onclick="closeWebSocket()">关闭WebSocket</button><br>
            <hr>
            <div id="message"></div>
        </div>
    </body>
    <script type="text/javascript">
    
        var websocket = null;
    
        function checkWebSocket(){
            if ("WebSocket" in window) {
                // alert("您的浏览器支持 WebSocket!");
                setMessageInnerHTML("您的浏览器支持 WebSocket!");
            }
            else {
                // 浏览器不支持 WebSocket
                // alert("您的浏览器不支持 WebSocket!");
                setMessageInnerHTML("您的浏览器不支持 WebSocket!");
            }
        }
    
        // 连接  WebSocket
        function connectWebSocket(){
            // 打开一个 web socket
            websocket = new WebSocket("ws://localhost:8080/websocket");
            websocket.onopen = function() {
                // Web Socket 已连接上,使用 send() 方法发送数据
                setMessageInnerHTML("WebSocket 已连接...");
            };
            websocket.onmessage = function(evt) {
                var received_msg = evt.data;
                setMessageInnerHTML("收到消息:" + received_msg);
            };
            websocket.onclose = function()
             {
                setMessageInnerHTML("WebSocket 已关闭...");
             };
        }
    
        // 向WebSocket服务端发送消息
        function sendWebSocket(){
            if (websocket){
                if (websocket.readyState == websocket.OPEN) {
                    var message = document.getElementById('txt').value;
                    websocket.send(message);
                } else {
                    setMessageInnerHTML("WebSocket 未连接...");
                }
            }else {
                setMessageInnerHTML("WebSocket 未创建...");
            }
        }
    
        // 监听窗口关闭事件,当窗口关闭时,主动去关闭websocket连接,防止连接还没断开就关闭窗口,server端会抛异常。
        window.onbeforeunload = function() {
            closeWebSocket();
        }
    
        // 关闭WebSocket连接
        function closeWebSocket() {
            websocket.close();
        }
    
        // 将消息显示在网页上
        function setMessageInnerHTML(innerHTML) {
            document.getElementById('message').innerHTML += innerHTML + '<br/>';
        }
    </script>
    </html>

    WebSocket 服务端Java实现

    1、新建一个Maven Web工程,导入依赖

    <dependency>
        <groupId>javax.websocket</groupId>
        <artifactId>javax.websocket-api</artifactId>
        <version>1.1</version>
        <scope>provided</scope>
    </dependency>
    
     <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-websocket</artifactId>
          </dependency>

    2、编辑一个WebSocket服务端类,MyWebSocket.class

    package com.test.websocket;
    
    import java.io.IOException;
    
    import javax.websocket.OnClose;
    import javax.websocket.OnError;
    import javax.websocket.OnMessage;
    import javax.websocket.OnOpen;
    import javax.websocket.Session;
    import javax.websocket.server.ServerEndpoint;
    
    @ServerEndpoint("/websocket")
    public class MyWebSocket {
    
        private Session session;
    
        /**
         * 连接建立后触发的方法
         */
        @OnOpen
        public void onOpen(Session session) {
            this.session = session;
            WebSocketMapUtil.put(session.getId(), this);
            System.out.println("-------- onOpen: 当前在线人数 " + WebSocketMapUtil.getOnlineCount() + ",连接人 " + session.getId() + " --------");
        }
    
        /**
         * 连接关闭后触发的方法
         */
        @OnClose
        public void onClose() {
            // 从map中删除
            WebSocketMapUtil.remove(session.getId());
            System.out.println("-------- onClose: 当前在线人数 " + WebSocketMapUtil.getOnlineCount() + ",关闭人 " + session.getId() + " --------");
        }
    
        /**
         * 接收到客户端消息时触发的方法
         */
        @OnMessage
        public void onMessage(String message, Session session) throws Exception {
            // 获取服务端到客户端的通道
            MyWebSocket myWebSocket = WebSocketMapUtil.get(session.getId());
            System.out.println("收到来自 " + session.getId() + " 的消息:" + message);
    
            // 返回消息给Web Socket客户端(浏览器)
            myWebSocket.sendMessageAll("服务端已收到消息:" + message);
        }
    
        /**
         * 发生错误时触发的方法
         */
        @OnError
        public void onError(Session session, Throwable error) {
            System.out.println("-------- onError: 当前在线人数 " + WebSocketMapUtil.getOnlineCount() + ",连接发生错误 " + session.getId() + "-"+ error.getMessage() + " --------");
            // error.printStackTrace();
        }
    
        /**
         * 给单个客户端发送消息
         * @param message
         * @param sessionId
         * @throws IOException
         */
        public void sendMessageSingle(String message, String sessionId) throws IOException {
    
            // session.getBasicRemote().sendText(message); 同步消息
            // session.getAsyncRemote().sendText(message); 异步消息
    
            MyWebSocket myWebSocket = WebSocketMapUtil.get(sessionId);
            if(myWebSocket != null) {
                myWebSocket.session.getBasicRemote().sendText(message);
            }
        }
    
        /**
         * 给所有客户端发送消息
         * @param message
         * @throws IOException
         */
        public void sendMessageAll(String message) throws IOException {
            for (MyWebSocket item : WebSocketMapUtil.getValues() ) {
                item.session.getAsyncRemote().sendText(message);
                System.out.println(item.session.getId());
                System.out.println(item.session.isSecure());
                System.out.println(item.session.isOpen());
            }
        }
    
    }
    View Code

     3、编辑一个WebSocket 工具类,WebSocketMapUtil.class

    package com.test.websocket;
    
    import java.util.Collection;
    import java.util.concurrent.ConcurrentHashMap;
    import java.util.concurrent.ConcurrentMap;
    
    public class WebSocketMapUtil {
    
        public static ConcurrentMap<String, MyWebSocket> webSocketMap = new ConcurrentHashMap<>();
    
        public static void put(String key, MyWebSocket myWebSocket) {
            webSocketMap.put(key, myWebSocket);
        }
    
        public static MyWebSocket get(String key) {
            return webSocketMap.get(key);
        }
    
        public static void remove(String key) {
            webSocketMap.remove(key);
        }
    
        public static Collection<MyWebSocket> getValues() {
            return webSocketMap.values();
        }
    
        public static int getOnlineCount() {
            return webSocketMap.size();
        }
    }
    View Code

    4、注入一个ServerEndpointExporterBean,该Bean会自动注册使用@ServerEndpoint注解申请的websocket endpoint,代码如下:

    @Component
    public class WebSocketConfig {
        @Bean
        public ServerEndpointExporter serverEndpointExporter(){
            return new ServerEndpointExporter();
        }
    }

    启动项目效果如下:

    注意:若是项目中集成了shiro会被拦截,页面控制台报302,找到项目中的shiro文件增加一行放行路径就可以了。

  • 相关阅读:
    JavaSript数组扁平化去重
    宝塔面板忘记登陆账号和密码怎么办
    宝塔shell脚本执行thinkphp命令行
    laravel设置中国时区
    Laravel-admin左侧菜单栏怎么默认展开打开
    install.sh: 115: install.sh: Syntax error: "(" unexpected (expecting "}")
    git生成密钥
    git 忽略提交某个指定的文件(不从版本库中删除)
    larael-admin汉化配置中文
    Nginx PHP-Fcgi中因PHP执行时间导致504无限循环中断
  • 原文地址:https://www.cnblogs.com/huigee/p/14628074.html
Copyright © 2020-2023  润新知