玩轉 SpringBoot 2 之整合 WebSocket 篇

前言

本文主要介紹如何在SpringBoot 2 中使用 WebSocket 的快速搭建教程,閱讀前需要你必須瞭解如何搭建 SpringBoot 項目。

在搭建前先來了解一下什麼是 WebSocket,WebSocket 簡單點說就是 HTML5 提供的基於 TCP 一種新的協議,它的作用就是:使瀏覽器和服務器只需要完成一次握手可以實現遊覽器和服務端的消息互相推送。具體詳細描述可以查看 菜鳥教程相關介紹

閒話少說,接下來就直接開整 WebSocket 快速演示實戰操作!

快速演示實戰操作

第一步:需要引入WebSocket 的依賴,具體代碼如下:

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

如果使用外置的 tomcat 需要引入 javaee-api 我們這裏不引入是因爲我們使用的是內置的tomcat,spring-boot 內置tomcat 包含拉 javaee-api 。

第二步:添加 WebSocket 配置類信息,也就是配置 WebSocket 服務端點。具體代碼如下:

package cn.lijunkui.springbootlearn.socket.config;
 
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.stereotype.Component;
import org.springframework.web.socket.server.standard.ServerEndpointExporter;
 
import javax.websocket.server.ServerEndpoint;
 
@Configuration
public class WebSocketConfig {
    @Bean
    public ServerEndpointExporter serverEndpointExporter(){
        return new ServerEndpointExporter();
    }
}

第三步: 開發創建連接、推送和處理消息的 Socket 處理類。

通過 @Controller + @ServerEndpoint("/訪問地址名稱") 註解 聲明創建類爲WebSocket 服務端點類。具體代碼如下:

@Controller
@ServerEndpoint("/websocket")
public class Socket {
}

通過 CopyOnWriteArraySet 存放 WebSocket 連接 Session,並在聲明 @OnOpen 的方法上獲取並存放 Session。具體代碼如下:

@Controller
@ServerEndpoint("/websocket")
public class Socket {
    /*websocket 客戶端會話 通過Session 向客戶端發送數據*/
    private Session session;
    /*線程安全set 存放每個客戶端處理消息的對象*/
    private static CopyOnWriteArraySet<Socket> webSocketSet = new CopyOnWriteArraySet();
    /*websocket 連接建立成功後進行調用*/
    @OnOpen
    public void onOpen(Session session) {
        this.session = session;
        webSocketSet.add(this);
        System.out.println("websocket 有新的鏈接"+webSocketSet.size());
    }
 }

定義監聽客戶端發送消息、Websocket 發生錯誤、WebSocket 連接關閉方法並通過 @OnMessage、@OnError,@OnClose進行聲明。具體代碼如下:

    /*WebSocket 連接關閉調用的方法*/
    @OnClose
    public void onClose() {
        webSocketSet.remove(this);
    }
    /*收到客戶端消息後調用的方法*/
    @OnMessage
    public void onMessage(String message) throws IOException {
        for (Socket socket : webSocketSet) {
            socket.session.getBasicRemote().sendText("自己嘎給自己嘎發的消息:"+message);
        }
    }
    /* WebSocket 發生錯誤時進行調用*/
    @OnError
    public void onError(Session session,Throwable error){
        error.printStackTrace();
    }

通過 Session.getBasicRemote().sendText(String message) 想客戶端推送消息。

    public void sendMessage(String message) throws IOException {
        for (Socket socket : webSocketSet) {
            socket.session.getBasicRemote().sendText(message);
        }
    }

詳細代碼如下:

package cn.lijunkui.socket;

import java.io.IOException;
import java.util.concurrent.CopyOnWriteArraySet;

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;

import org.springframework.stereotype.Controller;
@Controller
@ServerEndpoint("/websocket")
public class Socket {
	/*websocket 客戶端會話 通過Session 向客戶端發送數據*/
    private Session session;
    /*線程安全set 存放每個客戶端處理消息的對象*/
    private static CopyOnWriteArraySet<Socket> webSocketSet = new CopyOnWriteArraySet();
    /*websocket 連接建立成功後進行調用*/
    @OnOpen
    public void onOpen(Session session) {
        this.session = session;
        webSocketSet.add(this);
        System.out.println("websocket 有新的鏈接"+webSocketSet.size());
    }
    /*WebSocket 連接關閉調用的方法*/
    @OnClose
    public void onClose() {
        webSocketSet.remove(this);
    }
    /*收到客戶端消息後調用的方法*/
    @OnMessage
    public void onMessage(String message) throws IOException {
        for (Socket socket : webSocketSet) {
            socket.session.getBasicRemote().sendText("自己嘎給自己嘎發的消息:"+message);
        }
    }
    /* WebSocket 發生錯誤時進行調用*/
    @OnError
    public void onError(Session session,Throwable error){
        error.printStackTrace();
    }
    public void sendMessage(String message) throws IOException {
        for (Socket socket : webSocketSet) {
            socket.session.getBasicRemote().sendText(message);
        }
    }
    public Session getSession() {
        return session;
    }
    public void setSession(Session session) {
        this.session = session;
    }
}

第四步:編寫遊覽器向服務端推送消息頁面 socket.html

通過 WebSocket(‘ws://WebScoket鏈接地址’) 對象建立和服務端的連接,並通過 WebSocket對象的 onopen、 onclose 、onmessage、onerror 來監聽 WebSocket 連接成功、關閉、服務隊推送消息、鏈接異常的信息。

通過 websocket.send(sendMessage) 方法向服務端推送消息。具體代碼如下:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>socket demo</title>
</head>
<body>
    <h2>學說話的tom 貓</h2>
    <div><label>消息內容(服務端接受到消息並原樣返回):</label><p id="serverMessage"></p></div>
    <div><label>我對自己說(遊覽器向服務端發送消息):</label><input id="sendMessage" type="text" />&nbsp;&nbsp;&nbsp;&nbsp;<button onclick="send()">發送</button></div>
 
<script type="text/javascript">
 
    var websocket = null;
    if('WebSocket' in window){
        websocket = new WebSocket('ws://localhost:8080/sbe/websocket');
    }else{
        alert("該遊覽器不知此WebSocket");
    }
    //websocket 鏈接成功後進行觸發
    websocket.onopen = function (event){
        console.log("建立鏈接。。。。");
    }
    //websocket 鏈接關閉的進行觸發
    websocket.onclose = function (event){
        console.log("關閉鏈接。。。。");
    }
    //接收到消息的進行觸發
    websocket.onmessage = function(event){
        var serverMessage = document.getElementById("serverMessage");
        serverMessage.innerHTML= event.data;
        console.log("收到消息");
    }
 
    //連接發生錯誤的進行觸發
    websocket.onerror = function(evt)
    {
        console.log("WebSocketError!");
    }
    //當窗口關閉時,主動去關閉websocket連接,防止連接還沒斷開就關閉窗口,server端會拋異常。
    window.onbeforeunload = function (){
        websocket.close();
    }
    //關閉websocket連接
    function closeWebSocket(){
        websocket.close();
    }
    //發送消息
    function send(){
        var sendIput = document.getElementById("sendMessage");
        var sendMessage = sendIput.value;
        if(sendMessage.trim() == ""){
            alert("請輸入發送消息!")
            return;
        }
        websocket.send(sendMessage);
    }
 
</script>
</body>
</html>

第五步:編寫通過後臺服務端發送消息到遊覽器的 Controller

package cn.lijunkui.springbootlearn.socket;
 
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
 
import java.io.IOException;
 
@RestController
@RequestMapping("socket")
public class SocketTestController {
    @Autowired
    private Socket socket;
    @GetMapping("/{message}")
    public void sendMessage(@PathVariable("message") String message) throws IOException {
        socket.sendMessage("這個是controller 發送的消息:"+message);
    }
}

第六步:編寫通過後臺發送消息到遊覽器的測試頁面 controllerTest.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
<h2>直接向服務端發送消息</h2>
<input type="text" id="sendMessage"><button onclick="serverSendMsg()">發送</button>
</body>
<script language="JavaScript">
    function serverSendMsg(){
        var sendIput = document.getElementById("sendMessage");
        var sendMessage = sendIput.value;
        if(sendMessage.trim() == ""){
            alert("請輸入發送消息!")
            return;
        }
        //創建異步對象
        var xhr = new XMLHttpRequest();
        //設置請求的類型及url
        xhr.open('get', 'http://localhost:8080/sbe/socket/'+sendMessage );
        //發送請求
        xhr.send();
        xhr.onreadystatechange = function () {
            // 這步爲判斷服務器是否正確響應
            if (xhr.readyState == 4 && xhr.status == 200) {
                console.log(xhr.responseText);
            }
        };
    }
</script>
</html>

測試

遊覽器向服務端發送消息測試:

打開遊覽器 訪問socket.html 訪問地址:http://localhost:8080/sbe/socket.html
在這裏插入圖片描述
直接向服務端發送消息測試

先訪問socket.html 訪問地址目的是先建立 WebSocket 的鏈接,然後訪問直接向服務端發送消息測試頁面,訪問地址:http://localhost:8080/sbe/controllerTest.html
在這裏插入圖片描述
在這裏插入圖片描述

需要注意的是 必須訪問 socket.html 建立連接後再訪問 controllerTest.html 進行發送消息,否則會發送失敗。

小結

SpringBoot 2 整合 WebSocket 主要分爲三步:

  1. 引入websocket starter 依賴 。
  2. 添加 WebSocket 服務端點配置類信息以及 WebSocket 消息處理類(建立連接 接受和發送消息)。
  3. 在HTML頁面通過 WebSocket 對象建立連接、接受和發送消息。

如果你還未曾使用過 WebSocket ,抓緊根據 快速演示實戰操作 實操一下吧!

代碼示例

我本地環境如下:

  • SpringBoot Version: 2.1.0.RELEASE
  • Apache Maven Version: 3.6.0
  • Java Version: 1.8.0_144
  • IDEA:Spring Tools Suite (STS)

整合過程如出現問題可以在我的GitHub 倉庫 springbootexamples 中模塊名爲 spring-boot-2.x-websocket 項目中進行對比查看

GitHub:https://github.com/zhuoqianmingyue/springbootexamples

參考文獻

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章