1. 程式人生 > 其它 >新版springboot2.x websocket實現

新版springboot2.x websocket實現

技術標籤:javawebsocketjavahtml

網上有很多關於WebSocket用法的文章,但是有一些內容錯誤或者程式碼不全的,這裡來個全的,方便自己記憶。

前言:

WebSocket是一種在單個TCP連線上進行全雙工通訊的協議。WebSocket通訊協議於2011年被IETF定為標準RFC 6455,並由RFC7936補充規範。WebSocketAPI也被W3C定為標準。WebSocket使得客戶端和伺服器之間的資料交換變得更加簡單,允許服務端主動向客戶端推送資料。在WebSocket API中,瀏覽器和伺服器只需要完成一次握手,兩者之間就直接可以建立永續性的連線,並進行雙向資料傳輸。

說明:

本篇主要介紹在SpringBoot框架下,WebSocket基於註解使用的3種場景:

1、自己給自己發訊息

2、自己給所有客戶端傳送訊息(不包括自己)

3、自己給另一個客戶端傳送訊息

工程目錄:

pom檔案中的依賴:

    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.4.0</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>
    <modelVersion>4.0.0</modelVersion>

    <artifactId>springboot-7-websocket</artifactId>
    <description> websocket learn...</description>

    <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
        <java.version>1.8</java.version>
    </properties>


    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <!-- websocket -->
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-websocket</artifactId>
        </dependency>
        <dependency>
            <groupId>com.yzgu.</groupId>
            <artifactId>common</artifactId>
            <version>1.0-SNAPSHOT</version>
        </dependency>
    </dependencies>
@Configuration
public class WebSocketConfig {
    /**
     * 注入一個ServerEndpointExporter,該Bean會自動註冊使用@ServerEndpoint註解申明的websocket endpoint
     */
    @Bean
    public ServerEndpointExporter serverEndpointExporter() {
        return new ServerEndpointExporter();
    }
}

@SpringBootApplication
public class AppApplication {
    public static void main(String[] args) {
        SpringApplication.run(AppApplication.class, args);
    }
}
public class MyMessage {

    private String userId;

    private String message;


    public String getUserId() {
        return userId;
    }

    public void setUserId(String userId) {
        this.userId = userId;
    }

    public String getMessage() {
        return message;
    }

    public void setMessage(String message) {
        this.message = message;
    }

    @Override
    public String toString() {
        return "{" +
                "userId='" + userId + '\'' +
                ", message='" + message + '\'' +
                '}';
    }
}
package com.yzgu.up.component;

import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;

import javax.websocket.*;
import javax.websocket.server.ServerEndpoint;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicInteger;

/**
 *
 * 前後端互動的類實現訊息的接收推送(自己傳送給所有人(不包括自己))
 *
 * @ServerEndpoint(value = "/test/oneToMany") 前端通過此URI 和後端互動,建立連線
 */
@Slf4j
@ServerEndpoint(value = "/test/oneToMany")
@Component
public class OneToManyWebSocket {

    /** 記錄當前線上連線數 */
    private static AtomicInteger onlineCount = new AtomicInteger(0);

    /** 存放所有線上的客戶端 */
    private static Map<String, Session> clients = new ConcurrentHashMap<>();

    /**
     * 連線建立成功呼叫的方法
     */
    @OnOpen
    public void onOpen(Session session) {
        onlineCount.incrementAndGet(); // 線上數加1
        clients.put(session.getId(), session);
        log.info("有新連線加入:{},當前線上人數為:{}", session.getId(), onlineCount.get());
    }

    /**
     * 連線關閉呼叫的方法
     */
    @OnClose
    public void onClose(Session session) {
        onlineCount.decrementAndGet(); // 線上數減1
        clients.remove(session.getId());
        log.info("有一連線關閉:{},當前線上人數為:{}", session.getId(), onlineCount.get());
    }

    /**
     * 收到客戶端訊息後呼叫的方法
     *
     * @param message
     *            客戶端傳送過來的訊息
     */
    @OnMessage
    public void onMessage(String message, Session session) {
        log.info("服務端收到客戶端[{}]的訊息:{}", session.getId(), message);
        this.sendMessage(message, session);
    }

    @OnError
    public void onError(Session session, Throwable error) {
        log.error("發生錯誤");
        error.printStackTrace();
    }

    /**
     * 群發訊息
     *
     * @param message
     *            訊息內容
     */
    private void sendMessage(String message, Session fromSession) {
        for (Map.Entry<String, Session> sessionEntry : clients.entrySet()) {
            Session toSession = sessionEntry.getValue();
            // 排除掉自己
            if (!fromSession.getId().equals(toSession.getId())) {
                log.info("服務端給客戶端[{}]傳送訊息{}", toSession.getId(), message);
                toSession.getAsyncRemote().sendText(message);
            }
        }
    }}
package com.yzgu.up.component;

import com.alibaba.fastjson.JSON;
import com.yzgu.up.MyMessage;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;

import javax.websocket.*;
import javax.websocket.server.ServerEndpoint;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicInteger;

/**
 * 前後端互動的類實現訊息的接收推送(自己傳送給另一個人)
 *
 * @ServerEndpoint(value = "/test/oneToOne") 前端通過此URI 和後端互動,建立連線
 */
@Slf4j
@ServerEndpoint(value = "/test/oneToOne")
@Component
public class OneToOneWebSocket {

    /** 記錄當前線上連線數 */
    private static AtomicInteger onlineCount = new AtomicInteger(0);

    /** 存放所有線上的客戶端 */
    private static Map<String, Session> clients = new ConcurrentHashMap<>();

    /**
     * 連線建立成功呼叫的方法
     */
    @OnOpen
    public void onOpen(Session session) {
        onlineCount.incrementAndGet(); // 線上數加1
        clients.put(session.getId(), session);
        log.info("有新連線加入:{},當前線上人數為:{}", session.getId(), onlineCount.get());
    }

    /**
     * 連線關閉呼叫的方法
     */
    @OnClose
    public void onClose(Session session) {
        onlineCount.decrementAndGet(); // 線上數減1
        clients.remove(session.getId());
        log.info("有一連線關閉:{},當前線上人數為:{}", session.getId(), onlineCount.get());
    }

    /**
     * 收到客戶端訊息後呼叫的方法
     *
     * @param message
     *            客戶端傳送過來的訊息
     */
    @OnMessage
    public void onMessage(String message, Session session) {
        log.info("服務端收到客戶端[{}]的訊息[{}]", session.getId(), message);
        try {

            // 測試時 傳送的訊息格式:  {"message":"ss222","userId":"1"}
            MyMessage myMessage = JSON.parseObject(message, MyMessage.class);
            if (myMessage != null) {
                Session toSession = clients.get(myMessage.getUserId());
                if (toSession != null) {
                    this.sendMessage(myMessage.getMessage(), toSession);
                }
            }
        } catch (Exception e) {
            log.error("解析失敗:{}", e);
        }
    }

    @OnError
    public void onError(Session session, Throwable error) {
        log.error("發生錯誤");
        error.printStackTrace();
    }

    /**
     * 服務端傳送訊息給客戶端
     */
    private void sendMessage(String message, Session toSession) {
        try {
            log.info("服務端給客戶端[{}]傳送訊息[{}]", toSession.getId(), message);
            toSession.getBasicRemote().sendText(message);
        } catch (Exception e) {
            log.error("服務端傳送訊息給客戶端失敗:{}", e);
        }
    }

}
package com.yzgu.up.component;

import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;

import javax.websocket.*;
import javax.websocket.server.ServerEndpoint;
import java.util.concurrent.atomic.AtomicInteger;

/**
 * 前後端互動的類實現訊息的接收推送(自己傳送給自己)
 *
 * @ServerEndpoint(value = "/test/one") 前端通過此URI和後端互動,建立連線
 */
@Slf4j
@ServerEndpoint(value = "/test/one")
@Component
public class OneWebSocket {

    /**
     * 記錄當前線上連線數
     */
    private static AtomicInteger onlineCount = new AtomicInteger(0);

    /**
     * 連線建立成功呼叫的方法
     */
    @OnOpen
    public void onOpen(Session session) {
        onlineCount.incrementAndGet(); // 線上數加1
        log.info("有新連線加入:{},當前線上人數為:{}", session.getId(), onlineCount.get());
    }

    /**
     * 連線關閉呼叫的方法
     */
    @OnClose
    public void onClose(Session session) {
        onlineCount.decrementAndGet(); // 線上數減1
        log.info("有一連線關閉:{},當前線上人數為:{}", session.getId(), onlineCount.get());
    }

    /**
     * 收到客戶端訊息後呼叫的方法
     *
     * @param message 客戶端傳送過來的訊息
     */
    @OnMessage
    public void onMessage(String message, Session session) {
        log.info("服務端收到客戶端[{}]的訊息:{}", session.getId(), message);
        this.sendMessage("Hello, " + message, session);
    }

    @OnError
    public void onError(Session session, Throwable error) {
        log.error("發生錯誤");
        error.printStackTrace();
    }

    /**
     * 服務端傳送訊息給客戶端
     */
    private void sendMessage(String message, Session toSession) {
        try {
            log.info("服務端給客戶端[{}]傳送訊息{}", toSession.getId(), message);
            toSession.getBasicRemote().sendText(message);
        } catch (Exception e) {
            log.error("服務端傳送訊息給客戶端失敗:{}", e);
        }
    }
}
<!DOCTYPE HTML>
<html>
<head>
    <title>My WebSocket</title>
</head>

<body>
<input id="text" type="text" />
<button onclick="send()">Send</button>
<button onclick="closeWebSocket()">Close</button>
<div id="message"></div>
</body>

<script type="text/javascript">
    var websocket = null;

    //判斷當前瀏覽器是否支援WebSocket, 主要此處要更換為自己的地址
    if ('WebSocket' in window) {
        websocket = new WebSocket("ws://localhost:18092/test/oneToOne");
    } else {
        alert('Not support websocket')
    }

    //連線發生錯誤的回撥方法
    websocket.onerror = function() {
        setMessageInnerHTML("error");
    };

    //連線成功建立的回撥方法
    websocket.onopen = function(event) {
        //setMessageInnerHTML("open");
    }

    //接收到訊息的回撥方法
    websocket.onmessage = function(event) {
        setMessageInnerHTML(event.data);
    }

    //連線關閉的回撥方法
    websocket.onclose = function() {
        setMessageInnerHTML("close");
    }

    //監聽視窗關閉事件,當視窗關閉時,主動去關閉websocket連線,防止連線還沒斷開就關閉視窗,server端會拋異常。
    window.onbeforeunload = function() {
        websocket.close();
    }

    //將訊息顯示在網頁上
    function setMessageInnerHTML(innerHTML) {
        document.getElementById('message').innerHTML += innerHTML + '<br/>';
    }

    //關閉連線
    function closeWebSocket() {
        websocket.close();
    }

    //傳送訊息
    function send() {
        var message = document.getElementById('text').value;
        websocket.send(message);
    }
</script>
</html>
server:
  port: 18092

個人比較懶,程式碼都貼了,你們注意下埠和地址對應 ,基本上就ok了。

直接訪問 index 測試就好了