SpringBoot引入WebSocket

鱼鱼寡欢發表於2024-07-06

WebSocket 是一種在客戶端和伺服器之間提供低延遲、全雙工通訊的網路協議。它允許雙方在建立一次連線後,進行實時、持續的資料交換,無需像HTTP那樣為每一個請求和響應建立新的連線。WebSocket的設計初衷是解決傳統HTTP協議在實時通訊方面的不足,比如實現實時聊天、遊戲、股票報價等需要高頻率、連續資料更新的應用場景。

1 構建服務端

1.1 引入依賴

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

1.2 建立配置類

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

1.3 建立服務端

import lombok.extern.slf4j.Slf4j;
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.PathParam;
import javax.websocket.server.ServerEndpoint;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.CopyOnWriteArraySet;

@Component
@Slf4j
@ServerEndpoint("/websocket/{userId}")
public class WebSocketTest {
    /**
     * 執行緒安全的無序的集合
     */
    private static final CopyOnWriteArraySet<Session> SESSIONS = new CopyOnWriteArraySet<>();

    /**
     * 儲存線上連線數
     */
    private static final Map<String, Session> SESSION_POOL = new HashMap<>();

    @OnOpen
    public void onOpen(Session session, @PathParam(value = "userId") String userId) {
        try {
            SESSIONS.add(session);
            SESSION_POOL.put(userId, session);
            log.info("【WebSocket訊息】有新的連線,總數為:" + SESSIONS.size());
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    @OnClose
    public void onClose(Session session) {
        try {
            SESSIONS.remove(session);
            log.info("【WebSocket訊息】連線斷開,總數為:" + SESSIONS.size());
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    @OnMessage
    public void onMessage(String message) {
        log.info("【WebSocket訊息】收到客戶端訊息:" + message);
        Session session = SESSION_POOL.get("123");
        if (session != null && session.isOpen()) {
            try {
                synchronized (session) {
                    log.info("【WebSocket訊息】單點訊息:" + message);
                    session.getAsyncRemote().sendText(message);
                }
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }

    /**
     * 此為廣播訊息
     *
     * @param message 訊息
     */
    public void sendAllMessage(String message) {
        log.info("【WebSocket訊息】廣播訊息:" + message);
        for (Session session : SESSIONS) {
            try {
                if (session.isOpen()) {
                    session.getAsyncRemote().sendText(message);
                }
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }

    /**
     * 此為單點訊息
     *
     * @param userId  使用者編號
     * @param message 訊息
     */
    public void sendOneMessage(String userId, String message) {
        Session session = SESSION_POOL.get(userId);
        if (session != null && session.isOpen()) {
            try {
                synchronized (session) {
                    log.info("【WebSocket訊息】單點訊息:" + message);
                    session.getAsyncRemote().sendText(message);
                }
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }

    /**
     * 此為單點訊息(多人)
     *
     * @param userIds 使用者編號列表
     * @param message 訊息
     */
    public void sendMoreMessage(String[] userIds, String message) {
        for (String userId : userIds) {
            Session session = SESSION_POOL.get(userId);
            if (session != null && session.isOpen()) {
                try {
                    log.info("【WebSocket訊息】單點訊息:" + message);
                    session.getAsyncRemote().sendText(message);
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        }
    }
}

2 建立客戶端

2.1 引入依賴

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

2.2 注入物件

在配置類中注入WebSocketClient

import lombok.extern.slf4j.Slf4j;
import org.java_websocket.client.WebSocketClient;
import org.java_websocket.handshake.ServerHandshake;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.socket.server.standard.ServerEndpointExporter;

import java.net.URI;

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

    @Bean
    public WebSocketClient webSocketClient() {
        WebSocketClient wsc = null;
        try {
            wsc = new WebSocketClient(new URI("ws://127.0.0.1:2580/websocket/123")) {
                @Override
                public void onOpen(ServerHandshake serverHandshake) {
                    log.info("與服務端建立連線");
                }

                @Override
                public void onMessage(String s) {
                    log.info("收到服務端的訊息:{}", s);
                }

                @Override
                public void onClose(int i, String s, boolean b) {
                    log.info("與服務端的連線斷開 code:{} reason:{} {}", i, s, b);
                }

                @Override
                public void onError(Exception e) {
                    log.info("連線報錯");
                }
            };
            wsc.connect();
            return wsc;
        } catch (Exception e) {
            e.printStackTrace();
        }
        return wsc;
    }
}

2.3 測試

import org.java_websocket.client.WebSocketClient;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

import javax.annotation.Resource;

@RestController
public class TestController {

    @Resource
    WebSocketClient wsClient;

    @GetMapping("/send")
    public void test(String text){
        wsClient.send(text);
    }
}

2.4 結果

瀏覽器訪問

http://localhost:2580/send?text=測試

結果

2024-07-06 15:38:28.586  INFO 25860 --- [nio-2580-exec-4] c.x.p.websocket.socket.WebSocketTest     : 【WebSocket訊息】收到客戶端訊息:測試
2024-07-06 15:38:28.586  INFO 25860 --- [nio-2580-exec-4] c.x.p.websocket.socket.WebSocketTest     : 【WebSocket訊息】單點訊息:測試
2024-07-06 15:38:28.587  INFO 25860 --- [ctReadThread-31] c.x.p.websocket.config.WebSocketConfig   : 收到服務端的訊息:測試

image

相關文章