SpringBoot集成WebSocket

懵懂的女人 提交于 2020-03-02 13:20:06

什么是WebSocket?

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

为什么需要 WebSocket?
初次接触 WebSocket 的人,都会问同样的问题:我们已经有了 HTTP 协议,为什么还需要另一个协议?它能带来什么好处?

答案很简单,因为 HTTP 协议有一个缺陷:通信只能由客户端发起,HTTP 协议做不到服务器主动向客户端推送信息。

举例来说,我们想要查询当前的排队情况,只能是页面轮询向服务器发出请求,服务器返回查询结果。轮询的效率低,非常浪费资源(因为必须不停连接,或者 HTTP 连接始终打开)。因此WebSocket 就是这样发明的。
 

maven依赖

SpringBoot2.0对WebSocket的支持简直太棒了,直接就有包可以引入

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

WebSocketConfig

启用WebSocket的支持也是很简单,几句代码搞定

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.socket.server.standard.ServerEndpointExporter;

/**
 * 开启WebSocket支持
 */
@Configuration  
public class WebSocketConfig {  
    
    @Bean  
    public ServerEndpointExporter serverEndpointExporter() {  
        return new ServerEndpointExporter();  
    }  
  

WebSocketServer
这就是重点了,核心都在这里。

因为WebSocket是类似客户端服务端的形式(采用ws协议),那么这里的WebSocketServer其实就相当于一个ws协议的Controller

package com.rz.handler;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;

import javax.websocket.OnClose;
import javax.websocket.OnMessage;
import javax.websocket.OnOpen;
import javax.websocket.Session;
import javax.websocket.server.ServerEndpoint;
import java.io.IOException;
import java.util.concurrent.CopyOnWriteArraySet;


@ServerEndpoint(value = "/websocket")
@Component
public class MyWebSocket {
    //静态变量,用来记录当前在线连接数。应该把它设计成线程安全的。
    private static int onlineCount = 0;

    //concurrent包的线程安全Set,用来存放每个客户端对应的MyWebSocket对象。
    public static CopyOnWriteArraySet<MyWebSocket> webSocketSet = new CopyOnWriteArraySet<MyWebSocket>();

    //与某个客户端的连接会话,需要通过它来给客户端发送数据
    private Session session;

    private static Logger log = LoggerFactory.getLogger("fileInfoLog");


    /**
     * 连接建立成功调用的方法*/
    @OnOpen
    public void onOpen(Session session) {
        this.session = session;
        webSocketSet.add(this);     //加入set中
        addOnlineCount();           //在线数加1
        log.info("有新连接加入!当前在线人数为" + getOnlineCount());
//        try {
//            sendMessage("有新连接加入!当前在线人数为" + getOnlineCount());
//        } catch (IOException e) {
//            System.out.println("IO异常");
//        }
    }

    /**
     * 连接关闭调用的方法
     */
    @OnClose
    public void onClose() {
        webSocketSet.remove(this);  //从set中删除
        subOnlineCount();           //在线数减1
        log.info("有一连接关闭!当前在线人数为" + getOnlineCount());
    }

    /**
     * 收到客户端消息后调用的方法
     *
     * @param message 客户端发送过来的消息*/
    @OnMessage
    public void onMessage(String message, Session session) throws IOException {
        this.session = session;
        message = "来自客户端的消息:" + message;
        log.info(message);
        try {
            sendMessage( message);
        } catch (IOException e) {
            log.error("onMessage方法异常"+e.toString());
            e.printStackTrace();
        }
        //群发消息
//        sendInfo("群发消息"+message);
    }

    /**
     * 发生错误时调用
     @OnError
     **/
     public void onError(Session session, Throwable error) {
         log.error("onMessage方法异常"+error.toString());
         error.printStackTrace();
     }


    /**
     * 发送消息需注意方法加锁synchronized,避免阻塞报错
     * 注意session.getBasicRemote()与session.getAsyncRemote()的区别
     * @param message
     * @throws IOException
     */
     public synchronized void sendMessage(String message) throws IOException {
//         this.session.getBasicRemote().sendText(message);
        this.session.getAsyncRemote().sendText(message);
     }


     /**
      * 群发自定义消息
      * */
    public static void sendInfo(String message) throws IOException {
        for (MyWebSocket item : webSocketSet) {
            try {
                item.sendMessage(message);
            } catch (IOException e) {
                continue;
            }
        }
    }

    public static synchronized int getOnlineCount() {
        return onlineCount;
    }

    public static synchronized void addOnlineCount() {
        MyWebSocket.onlineCount++;
    }

    public static synchronized void subOnlineCount() {
        MyWebSocket.onlineCount--;
    }


}

消息推送

至于推送新信息,可以再自己的Controller,引入webSocket的bean,在调用sendInfo();即可

@Autowired
private MyWebSocket myWebSocket;

示例:

页面发起

页面用js代码调用websocket,当然,太古老的浏览器是不行的,一般新的浏览器或者谷歌浏览器是没问题的。还有一点,记得协议是ws的,如果使用了一些路径类,可以replace(“http”,“ws”)来替换协议。

    var lockReconnect = false;//避免重复连接
    var ws = null; //WebSocket的引用
    var wsUrl = "192.168.88.145:7777/websocket"; //这个要与后端提供的相同
    //创建WebSocket连接,如果不确定浏览器是否支持,可以使用socket.js做连接
    function createWebSocket(url){
        try {
            if ('WebSocket' in window) {
                ws = new WebSocket("ws://" + url);
            }
            initEventHandle();
        } catch (e) {
            reconnect(wsUrl);
        }
    }

    function reconnect(url) {
        if(lockReconnect) return;
        lockReconnect = true;
        //没连接上会一直重连,设置延迟避免请求过多
        setTimeout(function () {
            createWebSocket(wsUrl);
            console.log("正在重连,当前时间"+new Date());
            lockReconnect = false;
        }, 5000); //这里设置重连间隔(ms)
    }

    /*********************初始化开始**********************/
    function initEventHandle() {
        // 连接成功建立后响应
        ws.onopen = function() {
            console.log("成功连接到" + wsUrl);
            //心跳检测重置
            heartCheck.reset().start();
        }
        // 收到服务器消息后响应
        ws.onmessage = function(e) {
            //如果获取到消息,心跳检测重置
            //拿到任何消息都说明当前连接是正常的
            heartCheck.reset().start();
            //Json转换成Object
            var msg =  e.data;
            if(msg.indexOf("data")!=-1){
                var result = eval('(' + msg.split("data-")[1] + ')');
                if(!jQuery.isEmptyObject(result) && result.facePeopleId){
                    addCapture(result);
                }
            }

        }

        // 连接关闭后响应
        ws.onclose = function() {
            console.log("关闭连接");
            reconnect(wsUrl);//重连
        }
        ws.onerror = function () {
            reconnect(wsUrl);//重连
        };
    }
    /***************初始化结束***********************/
        //心跳检测
    var heartCheck = {
            timeout: 15000,//毫秒
            timeoutObj: null,
            serverTimeoutObj: null,
            reset: function(){
                clearTimeout(this.timeoutObj);
                clearTimeout(this.serverTimeoutObj);
                return this;
            },
            start: function(){
                var self = this;
                this.timeoutObj = setTimeout(function(){
                    //这里发送一个心跳,后端收到后,返回一个心跳消息,
                    //onmessage拿到返回的心跳就说明连接正常
                    ws.send("HeartBeat");
                    self.serverTimeoutObj = setTimeout(function(){//如果超过一定时间还没重置,说明后端主动断开了
                        ws.close();//如果onclose会执行reconnect,我们执行ws.close()就行了.如果直接执行reconnect 会触发onclose导致重连两次
                    }, self.timeout)
                }, this.timeout)
            }
        }

// 强制退出
    window.onunload = function() {
        ws.close();
    }
    createWebSocket(wsUrl);/**启动连接**/
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!