SpringBoot2.0整合WebSocket,實(shí)現(xiàn)后端數(shù)據(jù)實(shí)時(shí)推送!

之前公司的某個(gè)系統(tǒng)為了實(shí)現(xiàn)推送技術(shù),所用的技術(shù)都是Ajax輪詢,這種方式瀏覽器需要不斷的向服務(wù)器發(fā)出請(qǐng)求,顯然這樣會(huì)浪費(fèi)很多的帶寬等資源,所以研究了下WebSocket,本文將詳細(xì)介紹下。

一、什么是WebSocket?

WebSocket是HTML5開始提供的一種在單個(gè)TCP連接上進(jìn)行全雙工通訊的協(xié)議,能更好的節(jié)省服務(wù)器資源和帶寬,并且能夠更實(shí)時(shí)地進(jìn)行通訊。

WebSocket 使得客戶端和服務(wù)器之間的數(shù)據(jù)交換變得更加簡(jiǎn)單,允許服務(wù)端主動(dòng)向客戶端推送數(shù)據(jù),在WebSocket API中,瀏覽器和服務(wù)器只需要完成一次握手,兩者之間就直接可以創(chuàng)建持久性的連接,并進(jìn)行雙向數(shù)據(jù)傳輸。

二、SpringBoot整合WebSocket

新建一個(gè)spring boot項(xiàng)目spring-boot-websocket,按照下面步驟操作。

  1. pom.xml引入jar包
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-websocket</artifactId>
</dependency>
  1. 新建WebSocket的配置類

這個(gè)配置類檢測(cè)帶注解@ServerEndpoint的bean并注冊(cè)它們,配置類代碼如下:

@Configuration
public class WebSocketConfig {
    /**
     * 給spring容器注入這個(gè)ServerEndpointExporter對(duì)象
     * 相當(dāng)于xml:
     * <beans>
     * <bean id="serverEndpointExporter" class="org.springframework.web.socket.server.standard.ServerEndpointExporter"/>
     * </beans>
     * <p>
     * 檢測(cè)所有帶有@serverEndpoint注解的bean并注冊(cè)他們。
     *
     * @return
     */
    @Bean
    public ServerEndpointExporter serverEndpointExporter() {
        System.out.println("我被注入了");
        return new ServerEndpointExporter();
    }
}
  1. 新建WebSocket的處理類

這個(gè)處理類需要使用@ServerEndpoint,這個(gè)類里監(jiān)聽連接的建立關(guān)閉、消息的接收等,具體代碼如下:


@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,用來(lái)存放每個(gè)客戶端對(duì)應(yīng)的Session對(duì)象。
    private static CopyOnWriteArraySet<Session> SessionSet = new CopyOnWriteArraySet<Session>();


    /**
     * 連接建立成功調(diào)用的方法
     */
    @OnOpen
    public void onOpen(Session session) {
        SessionSet.add(session);
        int cnt = OnlineCount.incrementAndGet(); // 在線數(shù)加1
        log.info("有連接加入,當(dāng)前連接數(shù)為:{}", cnt);
        SendMessage(session, "連接成功");
    }

    /**
     * 連接關(guān)閉調(diào)用的方法
     */
    @OnClose
    public void onClose(Session session) {
        SessionSet.remove(session);
        int cnt = OnlineCount.decrementAndGet();
        log.info("有連接關(guān)閉,當(dāng)前連接數(shù)為:{}", cnt);
    }

    /**
     * 收到客戶端消息后調(diào)用的方法
     *
     * @param message
     *            客戶端發(fā)送過(guò)來(lái)的消息
     */
    @OnMessage
    public void onMessage(String message, Session session) {
        log.info("來(lái)自客戶端的消息:{}",message);
        SendMessage(session, "收到消息,消息內(nèi)容:"+message);

    }

    /**
     * 出現(xiàn)錯(cuò)誤
     * @param session
     * @param error
     */
    @OnError
    public void onError(Session session, Throwable error) {
        log.error("發(fā)生錯(cuò)誤:{},Session ID: {}",error.getMessage(),session.getId());
        error.printStackTrace();
    }

    /**
     * 發(fā)送消息,實(shí)踐表明,每次瀏覽器刷新,session會(huì)發(fā)生變化。
     * @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("發(fā)送消息出錯(cuò):{}", e.getMessage());
            e.printStackTrace();
        }
    }

    /**
     * 群發(fā)消息
     * @param message
     * @throws IOException
     */
    public static void BroadCastInfo(String message) throws IOException {
        for (Session session : SessionSet) {
            if(session.isOpen()){
                SendMessage(session, message);
            }
        }
    }

    /**
     * 指定Session發(fā)送消息
     * @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的會(huì)話:{}",sessionId);
        }
    }
}
  1. 新建一個(gè)html

目前大部分瀏覽器支持WebSocket,比如Chrome, Mozilla,Opera和Safari,在html頁(yè)面進(jìn)行websocket的連接建立、收消息的監(jiān)聽,頁(yè)面代碼如下:

<html>
<head>
    <meta charset="UTF-8">
    <title>websocket測(cè)試</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測(cè)試,客戶端接收到的消息如下:</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");
        //實(shí)現(xiàn)化WebSocket對(duì)象
        //指定要連接的服務(wù)器地址與端口建立連接
        //注意ws、wss使用不同的端口。我使用自簽名的證書測(cè)試,
        //無(wú)法使用wss,瀏覽器打開WebSocket時(shí)報(bào)錯(cuò)
        //ws對(duì)應(yīng)http、wss對(duì)應(yīng)https。
        socket = new WebSocket("ws://localhost:8080/ws/asset");
        //連接打開事件
        socket.onopen = function() {
            console.log("Socket 已打開");
            socket.send("消息發(fā)送測(cè)試(From Client)");
        };
        //收到消息事件
        socket.onmessage = function(msg) {
            $("#messageId").append(msg.data+ "\n");
            console.log(msg.data  );
        };
        //連接關(guān)閉事件
        socket.onclose = function() {
            console.log("Socket已關(guān)閉");
        };
        //發(fā)生了錯(cuò)誤事件
        socket.onerror = function() {
            alert("Socket發(fā)生了錯(cuò)誤");
        }
        //窗口關(guān)閉時(shí),關(guān)閉連接
        window.unload=function() {
            socket.close();
        };
    }
</script>

</body>
</html>

三、查看運(yùn)行效果

啟動(dòng)SpringBoot項(xiàng)目

  1. 打開首頁(yè)

本地瀏覽器打開首頁(yè)http://localhost:8080/,出現(xiàn)WebSocket測(cè)試頁(yè)面,同時(shí)后臺(tái)打印連接的日志。

有連接加入,當(dāng)前連接數(shù)為:1,sessionId=0
  1. 往客戶端發(fā)送消息

通過(guò)上面日志可以看到客戶端連接連接的sessionId,我測(cè)試時(shí)候sessionId是0,然后瀏覽器訪問(wèn)下面接口即可往客戶端發(fā)送消息。

//參數(shù)說(shuō)明: id:sessionID 
//參數(shù)說(shuō)明: message:消息內(nèi)容
http://localhost:8080/api/ws/sendOne?id=0&message=你好Java碎碎念
發(fā)送消息動(dòng)圖

到此SpringBoot整合WebSocket的功能已經(jīng)全部實(shí)現(xiàn),有問(wèn)題歡迎留言溝通哦!

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

推薦閱讀

1.一分鐘帶你了解下MyBatis的動(dòng)態(tài)SQL!

2.一分鐘帶你了解下Spring Security!

3.一分鐘帶你學(xué)會(huì)利用mybatis-generator自動(dòng)生成代碼!

4.手把手帶你實(shí)戰(zhàn)下Spring的七種事務(wù)傳播行為

5.SpringBoot系列-整合Mybatis(注解方式)


如果覺得文章不錯(cuò),希望可以隨手轉(zhuǎn)發(fā)或者”在看“哦,非常感謝哈!

關(guān)注下方公眾號(hào)后回復(fù)「1024」,有驚喜哦!

本文由博客一文多發(fā)平臺(tái) OpenWrite 發(fā)布!

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容