最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

Spring結(jié)合WebSocket實現(xiàn)實時通信的教程詳解

 更新時間:2024年01月30日 09:39:00   作者:lucky_fd  
WebSocket?是基于TCP/IP協(xié)議,獨立于HTTP協(xié)議的通信協(xié)議,本文將使用Spring結(jié)合WebSocket實現(xiàn)實時通信功能,有需要的小伙伴可以參考一下

簡介

WebSocket 是基于TCP/IP協(xié)議,獨立于HTTP協(xié)議的通信協(xié)議。WebSocket 連接允許客戶端和服務(wù)器之間的全雙工通信,以便任何一方都可以通過已建立的連接將數(shù)據(jù)推送到另一方。

我們常用的HTTP是客戶端通過「請求-響應(yīng)」的方式與服務(wù)器建立通信的,必須是客戶端主動觸發(fā)的行為,服務(wù)端只是做好接口被動等待請求。而在某些場景下的動作,是需要服務(wù)端主動觸發(fā)的,比如向客戶端發(fā)送消息、實時通訊、遠程控制等。客戶端是不知道這些動作幾時觸發(fā)的,假如用HTTP的方式,那么設(shè)備端需要不斷輪詢服務(wù)端,這樣的方式對服務(wù)器壓力太大,同時產(chǎn)生很多無效請求,且具有延遲性。于是才采用可以建立雙向通訊的長連接協(xié)議。通過握手建立連接后,服務(wù)端可以實時發(fā)送數(shù)據(jù)與指令到設(shè)備端,服務(wù)器壓力小。

Spring WebSocket是Spring框架的一部分,提供了在Web應(yīng)用程序中實現(xiàn)實時雙向通信的能力。本教程將引導你通過一個簡單的例子,演示如何使用Spring WebSocket建立一個實時通信應(yīng)用。

準備工作

確保你的項目中已經(jīng)引入了Spring框架的WebSocket模塊。你可以通過Maven添加以下依賴:

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

創(chuàng)建WebSocket配置類(實現(xiàn)WebSocketConfigurer接口)

首先,創(chuàng)建一個配置類,用于配置WebSocket的相關(guān)設(shè)置。

package com.ci.erp.human.config;

import com.ci.erp.human.handler.WebSocketHandler;
import com.ci.erp.human.interceptor.WebSocketHandleInterceptor;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.socket.config.annotation.EnableWebSocket;
import org.springframework.web.socket.config.annotation.EnableWebSocketMessageBroker;
import org.springframework.web.socket.config.annotation.WebSocketConfigurer;
import org.springframework.web.socket.config.annotation.WebSocketHandlerRegistry;

/**
 *
 * Websocket配置類
 *
 * @author lucky_fd
 * @since 2024-01-17
 */
@Configuration
@EnableWebSocket
public class WebSocketConfig implements WebSocketConfigurer {

    @Override
    public void registerWebSocketHandlers(WebSocketHandlerRegistry registry) {
        // 注冊websocket處理器和攔截器
        registry.addHandler(webSocketHandler(), "/websocket/server")
                .addInterceptors(webSocketHandleInterceptor()).setAllowedOrigins("*");
        registry.addHandler(webSocketHandler(), "/sockjs/server").setAllowedOrigins("*")
                .addInterceptors(webSocketHandleInterceptor()).withSockJS();
    }

    @Bean
    public WebSocketHandler webSocketHandler() {
        return new WebSocketHandler();
    }

    @Bean
    public WebSocketHandleInterceptor webSocketHandleInterceptor() {
        return new WebSocketHandleInterceptor();
    }
}

上面的配置類使用@EnableWebSocket注解啟用WebSocket,并通過registerWebSocketHandlers方法注冊WebSocket處理器。

  • registerWebSocketHandlers:這個方法是向spring容器注冊一個handler處理器及對應(yīng)映射地址,可以理解成MVC的Handler(控制器方法),websocket客戶端通過請求的url查找處理器進行處理
  • addInterceptors:攔截器,當建立websocket連接的時候,我們可以通過繼承spring的HttpSessionHandshakeInterceptor來做一些事情。
  • setAllowedOrigins:跨域設(shè)置,*表示所有域名都可以,不限制, 域包括ip:port, 指定*可以是任意的域名,不加的話默認localhost+本服務(wù)端口
  • withSockJS: 這個是應(yīng)對瀏覽器不支持websocket協(xié)議的時候降級為輪詢的處理。

創(chuàng)建WebSocket消息處理器(實現(xiàn)TextWebSocketHandler 接口)

接下來,創(chuàng)建一個消息處理器,處理客戶端發(fā)送的消息。

package com.ci.erp.human.handler;

import cn.hutool.core.util.ObjectUtil;
import com.ci.erp.common.core.utils.JsonUtils;
import com.ci.erp.human.domain.thirdVo.YYHeartbeat;
import org.springframework.web.socket.CloseStatus;
import org.springframework.web.socket.TextMessage;
import org.springframework.web.socket.WebSocketSession;
import org.springframework.web.socket.handler.TextWebSocketHandler;

import java.io.IOException;
import java.util.HashMap;
import java.util.Map;

/**
 *
 * websocket處理類
 * 實現(xiàn)WebSocketHandler接口
 *
 * - websocket建立連接后執(zhí)行afterConnectionEstablished回調(diào)接口
 * - websocket關(guān)閉連接后執(zhí)行afterConnectionClosed回調(diào)接口
 * - websocket接收客戶端消息執(zhí)行handleTextMessage接口
 * - websocket傳輸異常時執(zhí)行handleTransportError接口
 *
 * @author lucky_fd
 * @since 2024-01-17
 */

public class WebSocketHandler extends TextWebSocketHandler {

    /**
     * 存儲websocket客戶端連接
     * */
    private static final Map<String, WebSocketSession> connections = new HashMap<>();

    /**
     * 建立連接后觸發(fā)
     * */
    @Override
    public void afterConnectionEstablished(WebSocketSession session) throws Exception {
        System.out.println("成功建立websocket連接");
        // 建立連接后將連接以鍵值對方式存儲,便于后期向客戶端發(fā)送消息
        // 以客戶端連接的唯一標識為key,可以通過客戶端發(fā)送唯一標識
        connections.put(session.getRemoteAddress().getHostName(), session);
        System.out.println("當前客戶端連接數(shù):" + connections.size());
    }

    /**
     * 接收消息
     * */
    @Override
    protected void handleTextMessage(WebSocketSession session, TextMessage message) throws Exception {
        System.out.println("收到消息: " + message.getPayload());
		
		// 收到客戶端請求消息后進行相應(yīng)業(yè)務(wù)處理,返回結(jié)果
        this.sendMessage(session.getRemoteAddress().getHostName(),new TextMessage("收到消息: " + message.getPayload()));
    }

    /**
     * 傳輸異常處理
     * */
    @Override
    public void handleTransportError(WebSocketSession session, Throwable exception) throws Exception {
        super.handleTransportError(session, exception);
    }

    /**
     * 關(guān)閉連接時觸發(fā)
     * */
    @Override
    public void afterConnectionClosed(WebSocketSession session, CloseStatus status) throws Exception {
        System.out.println("觸發(fā)關(guān)閉websocket連接");
        // 移除連接
        connections.remove(session.getRemoteAddress().getHostName());
    }

    @Override
    public boolean supportsPartialMessages() {
        return super.supportsPartialMessages();
    }

    /**
     * 向連接的客戶端發(fā)送消息
     *
     * @author lucky_fd
     * @param clientId 客戶端標識
     * @param message 消息體
     **/
    public void sendMessage(String clientId, TextMessage message) {
        for (String client : connections.keySet()) {
            if (client.equals(clientId)) {
                try {
                    WebSocketSession session = connections.get(client);
                    // 判斷連接是否正常
                    if (session.isOpen()) {
                        session.sendMessage(message);
                    }
                } catch (IOException e) {
                    System.out.println(e.getMessage());
                }
                break;
            }
        }
    }
}

通過消息處理器,在開發(fā)中我們就可以實現(xiàn)向指定客戶端或所有客戶端發(fā)送消息,實現(xiàn)相應(yīng)業(yè)務(wù)功能。

創(chuàng)建攔截器

攔截器會在握手時觸發(fā),可以用來進行權(quán)限驗證

package com.ci.erp.human.interceptor;

import org.springframework.http.server.ServerHttpRequest;
import org.springframework.http.server.ServerHttpResponse;
import org.springframework.web.socket.WebSocketHandler;
import org.springframework.web.socket.server.support.HttpSessionHandshakeInterceptor;

import java.util.Map;

/**
 *
 * Websocket攔截器類
 *
 * @author lucky_fd
 * @since 2024-01-17
 */

public class WebSocketHandleInterceptor extends HttpSessionHandshakeInterceptor {

    @Override
    public boolean beforeHandshake(ServerHttpRequest request, ServerHttpResponse response, WebSocketHandler wsHandler, Map<String, Object> attributes) throws Exception {
        System.out.println("攔截器前置觸發(fā)");
        return super.beforeHandshake(request, response, wsHandler, attributes);
    }

    @Override
    public void afterHandshake(ServerHttpRequest request, ServerHttpResponse response, WebSocketHandler wsHandler, Exception ex) {
        System.out.println("攔截器后置觸發(fā)");
        super.afterHandshake(request, response, wsHandler, ex);
    }
}

創(chuàng)建前端頁面客戶端

最后,創(chuàng)建一個簡單的HTML頁面,用于接收用戶輸入并顯示實時聊天信息。

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Spring WebSocket Chat</title>
    <script src="https://code.jquery.com/jquery-3.6.4.min.js"></script>
    <script src="http://cdn.bootcss.com/sockjs-client/1.1.1/sockjs.js"></script>
</head>
<body>

請輸入:<input type="text" id="message" placeholder="Type your message">
<button onclick="sendMessage()">Send</button>
<button onclick="websocketClose()">關(guān)閉連接</button>
<div id="chat"></div>

<script>
    var socket = null;
    if ('WebSocket' in window) {
    	// 后端服務(wù)port為22900
        socket = new WebSocket("ws://localhost:22900/websocket/server");
    } else if ('MozWebSocket' in window) {
        socket = new MozWebSocket("ws://localhost:22900/websocket/server");
    } else {
        socket = new SockJS("http://localhost:22900/sockjs/server");
    }

    // 接收消息觸發(fā)
    socket.onmessage = function (event) {
        showMessage(event.data);
    };
    // 創(chuàng)建連接觸發(fā)
    socket.onopen = function (event) {
        console.log(event.type);
    };
    // 連接異常觸發(fā)
    socket.onerror = function (event) {
        console.log(event)
    };
    // 關(guān)閉連接觸發(fā)
    socket.onclose = function (closeEvent) {
        console.log(closeEvent.reason);
    };

    //發(fā)送消息
    function sendMessage() {
        if (socket.readyState === socket.OPEN) {
            var message = document.getElementById('message').value;
            socket.send(message);
            console.log("發(fā)送成功!");
        } else {
            console.log("連接失敗!");
        }

    }

    function showMessage(message) {
        document.getElementById('chat').innerHTML += '<p>' + message + '</p>';
    }

    function websocketClose() {
        socket.close();
        console.log("連接關(guān)閉");
    }

    window.close = function () {
        socket.onclose();
    };

</script>

</body>
</html>

這個頁面使用了WebSocket對象來建立連接,并通過onmessage監(jiān)聽收到的消息。通過輸入框發(fā)送消息,將會在頁面上顯示。

測試結(jié)果:

后端日志:

前端界面:

Java客戶端

添加依賴

<dependency>
      <groupId>org.java-websocket</groupId>
      <artifactId>Java-WebSocket</artifactId>
      <version>1.4.0</version>
</dependency>

創(chuàng)建客戶端類(繼承WebsocketClient)

package com.river.websocket;
 
import org.java_websocket.client.WebSocketClient;
import org.java_websocket.handshake.ServerHandshake;
 
import java.net.URI;
import java.net.URISyntaxException;
 
public class MyWebSocketClient extends WebSocketClient {
 
    MyWebSocketClient(String url) throws URISyntaxException {
        super(new URI(url));
    }
 	// 建立連接
    @Override
    public void onOpen(ServerHandshake shake) {
        System.out.println(shake.getHttpStatusMessage());
    }
 	// 接收消息
    @Override
    public void onMessage(String paramString) {
        System.out.println(paramString);
    }
 	// 關(guān)閉連接
    @Override
    public void onClose(int paramInt, String paramString, boolean paramBoolean) {
        System.out.println("關(guān)閉");
    }
 	// 連接異常
    @Override
    public void onError(Exception e) {
        System.out.println("發(fā)生錯誤");
    }
}

測試websocket

package com.river.websocket;
 
import org.java_websocket.enums.ReadyState;
 
import java.net.URISyntaxException;
 
/**
 * @author lucky_fd
 * @date 2024-1-17
 */
public class Client {
    public static void main(String[] args) throws URISyntaxException, InterruptedException {
        MyWebSocketClient client = new MyWebSocketClient("ws://localhost:22900/websocket/server");
        client.connect();
        while (client.getReadyState() != ReadyState.OPEN) {
            System.out.println("連接狀態(tài):" + client.getReadyState());
            Thread.sleep(100);
        }
        client.send("測試數(shù)據(jù)!");
        client.close();
    }
}

到此這篇關(guān)于Spring結(jié)合WebSocket實現(xiàn)實時通信的教程詳解的文章就介紹到這了,更多相關(guān)Spring WebSocket實時通信內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • 圖解Eclipse j2ee開發(fā)環(huán)境的搭建過程

    圖解Eclipse j2ee開發(fā)環(huán)境的搭建過程

    這篇文章以圖文結(jié)合的方式介紹了Eclipse j2ee開發(fā)環(huán)境的搭建過程,內(nèi)容很詳細,每一個步驟都有對應(yīng)的操作截圖,需要的朋友可以參考下
    2015-08-08
  • Java線程等待用法實例分析

    Java線程等待用法實例分析

    這篇文章主要介紹了Java線程等待用法,結(jié)合實例形式分析了obj.wait()實現(xiàn)線程等待相關(guān)原理與操作技巧,需要的朋友可以參考下
    2018-09-09
  • mybatis批量update時報錯multi-statement not allow的問題

    mybatis批量update時報錯multi-statement not allow的問題

    這篇文章主要介紹了mybatis批量update時報錯multi-statement not allow的問題及解決方案,具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教
    2023-10-10
  • Java分布式事務(wù)實現(xiàn)原理與解決方案詳解

    Java分布式事務(wù)實現(xiàn)原理與解決方案詳解

    分布式事務(wù)是分布式系統(tǒng)中的核心挑戰(zhàn)之一,它確保跨多個獨立服務(wù)或數(shù)據(jù)源的操作能夠保持原子性、一致性、隔離性和持久性(ACID),本文給大家介紹Java生態(tài)中分布式事務(wù)的實現(xiàn)原理、主流解決方案及其適用場景,感興趣的朋友一起看看吧
    2025-09-09
  • Java多線程Future實現(xiàn)優(yōu)雅獲取線程的執(zhí)行結(jié)果

    Java多線程Future實現(xiàn)優(yōu)雅獲取線程的執(zhí)行結(jié)果

    這篇文章主要為大家詳細介紹了Java如何利用Future實現(xiàn)優(yōu)雅獲取線程的執(zhí)行結(jié)果,文中的示例代碼講解詳細,感興趣的小伙伴可以跟隨小編一起學習一下
    2023-07-07
  • Java大對象存儲之@Lob注解處理BLOB和CLOB數(shù)據(jù)的方法

    Java大對象存儲之@Lob注解處理BLOB和CLOB數(shù)據(jù)的方法

    本文將深入探討@Lob注解的使用方法、最佳實踐以及在處理大對象存儲時應(yīng)當注意的性能與內(nèi)存考量,我們將通過實際示例展示如何在Java應(yīng)用中有效地管理和操作BLOB和CLOB數(shù)據(jù),感興趣的朋友一起看看吧
    2025-05-05
  • Java中Arraylist動態(tài)擴容方法詳解

    Java中Arraylist動態(tài)擴容方法詳解

    ArrayList的列表對象實質(zhì)上是存儲在一個引用型數(shù)組里的,下面這篇文章主要給大家介紹了關(guān)于Java中Arraylist動態(tài)擴容方法的相關(guān)資料,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面跟著小編來一起學習學習吧。
    2017-08-08
  • Java線程中synchronized和volatile關(guān)鍵字的區(qū)別詳解

    Java線程中synchronized和volatile關(guān)鍵字的區(qū)別詳解

    這篇文章主要介紹了Java線程中synchronized和volatile關(guān)鍵字的區(qū)別詳解,synchronzied既能夠保障可見性,又能保證原子性,而volatile只能保證可見性,無法保證原子性,volatile不需要加鎖,比synchronized更輕量級,不會阻塞線程,需要的朋友可以參考下
    2024-01-01
  • java中的transient關(guān)鍵字解讀

    java中的transient關(guān)鍵字解讀

    這篇文章主要介紹了java中的transient關(guān)鍵字解讀,transient關(guān)鍵字的主要作用就是讓某些被transient關(guān)鍵字修飾的成員屬性變量不被序列化,實際上也正是因此,在學習過程中很少用得上序列化操作,一般都是在實際開發(fā)中,需要的朋友可以參考下
    2023-09-09
  • MyBatis-Plus?ORM數(shù)據(jù)庫和實體類映射方式

    MyBatis-Plus?ORM數(shù)據(jù)庫和實體類映射方式

    本文詳細介紹了MyBatis-Plus(MP)在數(shù)據(jù)庫和Java對象映射方面的功能,包括基本映射、主鍵生成策略、復雜映射(如嵌套對象和集合類型)以及自定義SQL的使用,MP通過豐富的注解和XML配置,簡化了數(shù)據(jù)庫操作,提高了開發(fā)效率
    2025-01-01

最新評論

德保县| 乐安县| 扎赉特旗| 论坛| 遂宁市| 广平县| 浦北县| 黑水县| 乌鲁木齐市| 东乡族自治县| 中西区| 虹口区| 筠连县| 甘谷县| 古蔺县| 攀枝花市| 晋宁县| 比如县| 武威市| 巴青县| 宣汉县| 博罗县| 永康市| 巨野县| 包头市| 佛冈县| 都昌县| 舞钢市| 突泉县| 白银市| 星子县| 峨山| 贵阳市| 南丰县| 阳原县| 江油市| 宿迁市| 三穗县| 依安县| 琼海市| 阿拉善盟|