SpringBoot實(shí)現(xiàn)WebSocket的示例代碼
一、環(huán)境搭建
1.創(chuàng)建SpringBoot項(xiàng)目,引入相關(guān)依賴
<dependencies>
<!-- Spring Boot核心啟動器,引入常用依賴基礎(chǔ) -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
<!-- Spring Boot對Thymeleaf模板引擎支持,用于視圖渲染 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
<!-- nekohtml庫,用于HTML解析,指定版本1.9.22 -->
<dependency>
<groupId>net.sourceforge.nekohtml</groupId>
<artifactId>nekohtml</artifactId>
<version>1.9.22</version>
</dependency>
<!-- JUnit 4測試框架依賴,僅測試階段用,版本4.13.2 -->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.13.2</version>
<scope>test</scope>
</dependency>
<!-- JUnit Jupiter(JUnit 5部分),用于測試,僅測試環(huán)境 -->
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter</artifactId>
<scope>test</scope>
</dependency>
<!-- Spring Boot Web開發(fā)啟動器,構(gòu)建Web應(yīng)用相關(guān) -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- Spring Boot測試相關(guān)依賴,確保應(yīng)用正確性等 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-test</artifactId>
</dependency>
<!-- Spring Boot對WebSocket啟動器,實(shí)現(xiàn)雙向通信功能 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-websocket</artifactId>
</dependency>
</dependencies>
2.在resources下邊創(chuàng)建static和templates文件夾
3.配置application.yml
spring:
thymeleaf:
cache: false # 關(guān)閉Thymeleaf頁面緩存,開發(fā)時(shí)便于即時(shí)看到模板修改效果
encoding: UTF-8 # 模板編碼設(shè)為UTF-8,確保字符正確解析,避免亂碼
prefix: classpath:/templates/ # 頁面映射路徑:模板文件查找路徑前綴,在類路徑下的templates目錄找
suffix:.html # 視圖對應(yīng)的模板文件后綴名
mode: HTML5 # 設(shè)置模板模式為HTML5,遵循HTML5規(guī)范解析處理
mvc:
pathmatch:
matching-strategy: ant_path_matcher # Spring MVC路徑匹配采用ant_path_matcher策略,更靈活處理URL路徑
static-path-pattern: /static/** # 定義靜態(tài)資源訪問路徑模式,通過/static/開頭的URL可訪問static目錄下靜態(tài)資源
二、配置類開啟WebSocket支持
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.socket.server.standard.ServerEndpointExporter;
/**
* @ Description: 開啟WebSocket支持
* 用于在Spring框架的應(yīng)用中配置和啟用WebSocket功能。
* 通過相關(guān)注解和方法的定義,使得應(yīng)用能夠正確地處理WebSocket連接和通信。
*/
@Configuration
public class WebSocketConfig {
@Bean //用于將方法返回的ServerEndpointExporter對象作為一個Bean注冊到Spring的容器中
public ServerEndpointExporter serverEndpointExporter() {
//創(chuàng)建并返回一個ServerEndpointExporter對象。
// ServerEndpointExporter主要作用是掃描帶有@ServerEndpoint注解的WebSocket端點(diǎn)類,并將它們注冊到Servlet容器中,
// 從而使得應(yīng)用能夠正確地處理WebSocket連接請求,實(shí)現(xiàn)WebSocket的通信功能。
return new ServerEndpointExporter();
}
}
三、服務(wù)層
import org.springframework.stereotype.Component;
import org.springframework.stereotype.Service;
import javax.websocket.*;
import javax.websocket.server.PathParam;
import javax.websocket.server.ServerEndpoint;
import java.io.IOException;
import java.util.concurrent.CopyOnWriteArraySet;
/**
* @ServerEndpoint 注解是一個類層次的注解,它的功能主要是將目前的類定義成一個 websocket 服務(wù)器端。
* 注解的值將被用于監(jiān)聽用戶連接的終端訪問 URL 地址,客戶端可以通過這個 URL 來連接到 WebSocket 服務(wù)器端
*/
@Component
@Service
/**@ServerEndpoint注解用于將當(dāng)前類定義為一個WebSocket服務(wù)器端端點(diǎn)。注解中的值"/api/websocket/{sid}"指定了客戶端連接到這個WebSocket服務(wù)器端的URL地址,
其中{sid}是一個路徑參數(shù),可以在后續(xù)的方法中獲取并使用,不同的客戶端可以通過帶有不同sid值的這個URL來建立與服務(wù)器的WebSocket連接。
*/
@ServerEndpoint("/api/websocket/{sid}")
public class WebSocketServer {
private Session session; //用戶信息
//存放每個客戶端對應(yīng)的MyWebSocket對象,保存所有已連接的客戶端對應(yīng)的WebSocketServer實(shí)例
private static CopyOnWriteArraySet<WebSocketServer> webSocketSet = new CopyOnWriteArraySet<WebSocketServer>();
//當(dāng)前在線連接數(shù),用于記錄當(dāng)前有多少個客戶端與WebSocket服務(wù)器建立了連接
private static int onlineCount = 0;
//用于接收從客戶端連接URL路徑參數(shù)中獲取的sid值,這個sid可以用于標(biāo)識不同的客戶端連接或者與客戶端相關(guān)的業(yè)務(wù)邏輯處理
private String sid = "";
/**
* 連接建立成功調(diào)用的方法
* 這個方法會處理與新連接建立相關(guān)的初始化操作,
* 比如記錄客戶端的會話信息、將當(dāng)前對象添加到已連接客戶端集合中、更新在線連接數(shù)等。
*/
@OnOpen
public void onOpen(Session session, @PathParam("sid") String sid) {
this.session = session;
webSocketSet.add(this); //加入set中,將當(dāng)前新建立連接的WebSocketServer對象添加到存放所有客戶端的集合中
this.sid = sid;
addOnlineCount(); //在線數(shù)加1,調(diào)用方法增加當(dāng)前的在線連接數(shù)統(tǒng)計(jì)值
try {
sendMessage("conn_success"); // 向當(dāng)前新連接的客戶端發(fā)送一條消息"conn_success",告知客戶端連接成功。
// 在控制臺打印出有新窗口開始監(jiān)聽的sid以及當(dāng)前的在線人數(shù)信息。
System.out.println("有新窗口開始監(jiān)聽:" + sid + ",當(dāng)前在線人數(shù)為:" + getOnlineCount());
} catch (IOException e) {
// 如果發(fā)送消息過程中出現(xiàn)IO異常,在控制臺打印出相應(yīng)提示信息
System.out.println("websocket IO Exception");
}
}
/**
* 連接關(guān)閉調(diào)用的方法
*/
// @OnClose注解標(biāo)記的方法會在WebSocket連接關(guān)閉時(shí)被自動調(diào)用。這個方法主要處理與連接關(guān)閉相關(guān)的清理操作,
// 比如從已連接客戶端集合中刪除對應(yīng)的對象、更新在線連接數(shù)等。
@OnClose
public void onClose() {
webSocketSet.remove(this); //從set中刪除,將當(dāng)前關(guān)閉連接的WebSocketServer對象從存放所有客戶端的集合中移除
subOnlineCount(); //在線數(shù)減1,調(diào)用方法減少當(dāng)前的在線連接數(shù)統(tǒng)計(jì)值
// 在控制臺打印出當(dāng)前關(guān)閉連接所對應(yīng)的sid值
System.out.println("釋放的sid為:"+sid);
// 在控制臺打印出有一連接關(guān)閉的提示信息以及更新后的當(dāng)前在線人數(shù)
System.out.println("有一連接關(guān)閉!當(dāng)前在線人數(shù)為" + getOnlineCount());
}
/**
* 接受到用戶信息后調(diào)用的方法
* @param message
* @param session
*/
// @OnMessage注解標(biāo)記的方法會在WebSocket服務(wù)器接收到客戶端發(fā)送的消息時(shí)被自動調(diào)用。這個方法主要負(fù)責(zé)處理接收到的消息,
// 比如在控制臺打印出消息來源及內(nèi)容,并且可以根據(jù)業(yè)務(wù)需求對消息進(jìn)行進(jìn)一步的處理,這里是將接收到的消息群發(fā)出去。
@OnMessage
public void onMessage(String message,Session session){
// 在控制臺打印出收到消息的來源窗口(通過sid標(biāo)識)以及消息的具體內(nèi)容
System.out.println("收到來自窗口" + sid + "的信息:" + message);
//群發(fā)消息
for (WebSocketServer item : webSocketSet) {
try {
//遍歷所有已連接的客戶端對應(yīng)的WebSocketServer對象,調(diào)用每個對象的sendMessage方法將接收到的消息發(fā)送給每個客戶端,實(shí)現(xiàn)群發(fā)功能
item.sendMessage(message);
} catch (IOException e) {
e.printStackTrace();
}
}
}
/**
* @ Param session
* @ Param error
*/
// @OnError注解標(biāo)記的方法會在WebSocket連接過程中出現(xiàn)錯誤時(shí)被自動調(diào)用。這個方法主要負(fù)責(zé)處理錯誤情況,
// 比如在控制臺打印出錯誤提示信息以及打印出詳細(xì)的錯誤堆棧信息,以便于排查問題
@OnError
public void onError(Session session, Throwable error) {
System.out.println("發(fā)生錯誤");
error.printStackTrace();
}
/**
* 實(shí)現(xiàn)服務(wù)器主動推送
*/
public void sendMessage(String message) throws IOException {
// 通過當(dāng)前客戶端的會話對象(this.session)獲取基本的遠(yuǎn)程通信端點(diǎn)(getBasicRemote),然后使用sendText方法將指定的消息發(fā)送給客戶端
this.session.getBasicRemote().sendText(message);
}
/**
* 群發(fā)自定義消息
*/
public static void sendInfo(String message, @PathParam("sid") String sid) throws IOException {
// 在控制臺打印出要推送消息的目標(biāo)窗口(通過sid標(biāo)識)以及推送的具體內(nèi)容
System.out.println("推送消息到窗口" + sid + ",推送內(nèi)容:" + message);
for (WebSocketServer item : webSocketSet) {
try {
//為null則全部推送
if (sid == null) {
item.sendMessage(message);
} else if (item.sid.equals(sid)) {
// 遍歷所有已連接的客戶端對應(yīng)的WebSocketServer對象,如果sid為null則表示要向所有客戶端推送消息,
// 如果當(dāng)前對象的sid與要推送的目標(biāo)sid相等,則調(diào)用該對象的sendMessage方法將消息發(fā)送給對應(yīng)的客戶端。
item.sendMessage(message);
}
} catch (IOException e) {
// 如果在發(fā)送消息給某個客戶端過程中出現(xiàn)IO異常,跳過當(dāng)前循環(huán),繼續(xù)嘗試給下一個客戶端發(fā)送消息。
continue;
}
}
}
// 以下這幾個方法用于對在線連接數(shù)(onlineCount)以及存放客戶端的集合(webSocketSet)進(jìn)行操作,
// 并且都使用了synchronized關(guān)鍵字來保證在多線程環(huán)境下對這些共享資源的操作是線程安全的。
/*該方法用于獲取當(dāng)前的在線連接數(shù)。
* 由于在線連接數(shù)(onlineCount)是一個被多個方法可能同時(shí)訪問和修改的共享變量,
* 為了確保在多線程環(huán)境下獲取到的在線連接數(shù)是準(zhǔn)確的,使用了synchronized關(guān)鍵字進(jìn)行同步。
* 這樣在某個線程調(diào)用此方法獲取在線連接數(shù)時(shí),其他線程不能同時(shí)對onlineCount進(jìn)行修改操作,保證了數(shù)據(jù)的一致性。
*/
public static synchronized int getOnlineCount() {
return onlineCount;
}
/**
* 該方法用于增加在線連接數(shù)。
* 當(dāng)有新的客戶端與WebSocket服務(wù)器成功建立連接時(shí)(如在onOpen方法中)會調(diào)用此方法。
* 同樣因?yàn)閛nlineCount是共享變量,多個線程可能同時(shí)嘗試增加它的值(比如多個客戶端同時(shí)連接),
* 使用synchronized關(guān)鍵字確保在同一時(shí)刻只有一個線程能夠執(zhí)行此方法對onlineCount進(jìn)行自增操作,
* 避免了數(shù)據(jù)不一致的情況,比如多個線程同時(shí)增加導(dǎo)致計(jì)數(shù)錯誤的問題。
*/
public static synchronized void addOnlineCount() {
WebSocketServer.onlineCount++;
}
/**
* 該方法用于減少在線連接數(shù)。
* 當(dāng)有客戶端與WebSocket服務(wù)器的連接關(guān)閉時(shí)(如在onClose方法中)會調(diào)用此方法。
* 與增加在線連接數(shù)的方法類似,為了保證在多線程環(huán)境下對onlineCount進(jìn)行準(zhǔn)確的自減操作,
* 使用synchronized關(guān)鍵字進(jìn)行同步,防止多個線程同時(shí)對其進(jìn)行操作而導(dǎo)致數(shù)據(jù)錯誤。
*/
public static synchronized void subOnlineCount() {
WebSocketServer.onlineCount--;
}
/**
* 該方法用于獲取存放所有已連接客戶端對應(yīng)的WebSocketServer對象的集合(webSocketSet)。
* 雖然這里獲取集合的操作相對簡單,但由于webSocketSet也是一個可能被多個線程訪問的共享資源,
* 使用synchronized關(guān)鍵字進(jìn)行同步,確保在獲取集合時(shí),其他線程不會對其進(jìn)行修改等操作,
* 從而保證獲取到的集合狀態(tài)是準(zhǔn)確的,可以安全地在獲取到集合后進(jìn)行后續(xù)的遍歷等操作(如在sendInfo方法中遍歷集合發(fā)送消息)
* @return
*/
public static CopyOnWriteArraySet<WebSocketServer> getWebSocketSet() {
return webSocketSet;
}
}
四、前端頁面
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Java 后端 WebSocket 的 Tomcat 實(shí)現(xiàn)</title>
<script src="https://code.jquery.com/jquery-3.1.1.min.js"></script>
</head>
<body>
Welcome<br/><input id="text" type="text" />
<button onclick="send()">發(fā)送消息</button>
<hr/>
<button onclick="closeWebSocket()">關(guān)閉WebSocket連接</button>
<hr/>
<div id="message"></div>
</body>
<script type="text/javascript">
// 定義一個全局變量websocket,用于存儲創(chuàng)建的WebSocket對象,初始值為null
var websocket = null;
//判斷當(dāng)前瀏覽器是否支持WebSocket
if('WebSocket' in window) {
// 如果瀏覽器支持WebSocket,創(chuàng)建一個WebSocket連接對象,連接到指定的服務(wù)器地址,
// 這里連接到本地的8080端口,路徑為/api/websocket/100,其中100可能是一個示例的連接標(biāo)識或參數(shù)
websocket = new WebSocket("ws://localhost:8080/api/websocket/100");
} else {
// 如果瀏覽器不支持WebSocket,彈出一個警告框,提示用戶當(dāng)前瀏覽器不支持WebSocket
alert('當(dāng)前瀏覽器 Not support websocket')
}
// 連接發(fā)生錯誤回調(diào)方法
// 當(dāng)WebSocket連接過程中出現(xiàn)錯誤時(shí),會自動調(diào)用此函數(shù)
websocket.onerror = function() {
// 在網(wǎng)頁上顯示"WebSocket連接發(fā)生錯誤"的提示信息
setMessageInnerHTML("WebSocket連接發(fā)生錯誤");
};
//連接成功建立回調(diào)方法
// 當(dāng)WebSocket連接成功建立時(shí),會自動調(diào)用此函數(shù)
websocket.onopen = function() {
// 在網(wǎng)頁上顯示"WebSocket連接成功"的提示信息
setMessageInnerHTML("WebSocket連接成功");
}
var U01data, Uidata, Usdata
//接收消息回調(diào)方法
// 當(dāng)WebSocket服務(wù)器發(fā)送消息過來時(shí),會自動調(diào)用此函數(shù)
websocket.onmessage = function(event) {
//在控制臺打印接收到的消息事件對象,用于調(diào)試查看消息的詳細(xì)信息
console.log(event);
// 將接收到的消息內(nèi)容顯示在網(wǎng)頁上
setMessageInnerHTML(event.data);
}
//連接關(guān)閉回調(diào)方法
// 當(dāng)WebSocket連接關(guān)閉時(shí),會自動調(diào)用此函數(shù)
websocket.onclose = function() {
setMessageInnerHTML("WebSocket連接關(guān)閉");
}
//監(jiān)聽窗口關(guān)閉事件
// 當(dāng)用戶嘗試關(guān)閉瀏覽器窗口時(shí),會自動調(diào)用此函數(shù)
window.onbeforeunload = function() {
// 調(diào)用closeWebSocket函數(shù),關(guān)閉當(dāng)前的WebSocket連接
closeWebSocket();
}
//將消息顯示在網(wǎng)頁上
function setMessageInnerHTML(innerHTML) {
//在控制臺打印要顯示在網(wǎng)頁上的內(nèi)容
console.log(innerHTML)
// 通過id獲取網(wǎng)頁上的div元素(id為"message"),并將傳入的內(nèi)容添加到該元素的innerHTML屬性中
document.getElementById('message').innerHTML += innerHTML + '<br/>';
}
//關(guān)閉WebSocket連接
function closeWebSocket() {
// 調(diào)用WebSocket對象的close方法,關(guān)閉當(dāng)前建立的WebSocket連接
websocket.close();
}
//發(fā)送消息
function send() {
// 通過id獲取網(wǎng)頁上文本輸入框(id為"text")中的值,即用戶輸入的消息內(nèi)容
var message = document.getElementById('text').value;
// 使用WebSocket對象的send方法,將用戶輸入的消息以特定格式(這里是一個包含"msg"字段的JSON字符串)發(fā)送給服務(wù)器
websocket.send('{"msg":"' + message + '"}');
// 調(diào)用setMessageInnerHTML函數(shù),將用戶輸入的消息顯示在網(wǎng)頁上,并添加一個換行符( )
setMessageInnerHTML(message + " ");
}
</script>
</html>五、運(yùn)行效果
兩個瀏覽器模擬兩個用戶對話:

控制臺

到此這篇關(guān)于SpringBoot實(shí)現(xiàn)WebSocket的示例代碼的文章就介紹到這了,更多相關(guān)SpringBoot實(shí)現(xiàn)WebSocket內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
- SpringBoot分布式WebSocket的實(shí)現(xiàn)指南
- SpringBoot實(shí)現(xiàn)WebSocket通信過程解讀
- 深入淺出SpringBoot WebSocket構(gòu)建實(shí)時(shí)應(yīng)用全面指南
- 利用SpringBoot與WebSocket實(shí)現(xiàn)實(shí)時(shí)雙向通信功能
- Springboot整合WebSocket 實(shí)現(xiàn)聊天室功能
- vue+springboot+webtrc+websocket實(shí)現(xiàn)雙人音視頻通話會議(最新推薦)
- Springboot使用Websocket的時(shí)候調(diào)取IOC管理的Bean報(bào)空指針異常問題
- Java?springBoot初步使用websocket的代碼示例
- SpringBoot3整合WebSocket詳細(xì)指南
- Spring Boot集成WebSocket項(xiàng)目實(shí)戰(zhàn)的示例代碼
相關(guān)文章
Java設(shè)計(jì)模式常用的七大原則總結(jié)
今天給大家總結(jié)了Java設(shè)計(jì)模式的七大原則,主要有單一職責(zé)原則,接口隔離原則,依賴倒轉(zhuǎn)原則,里氏替換原則等,文中有非常詳細(xì)的介紹,需要的朋友可以參考下2021-06-06
基于Springboot2.3訪問本地路徑下靜態(tài)資源的方法(解決報(bào)錯:Not allowed to load local
這篇文章主要介紹了基于Springboot2.3訪問本地路徑下靜態(tài)資源的方法(解決報(bào)錯:Not allowed to load local resource),文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2020-08-08
spring-boot-maven-plugin引入不成功的解決方案
解決Spring Boot Maven插件問題:刪除倉庫文件夾或添加指定版本號,不刪除插件會導(dǎo)致打包路徑錯誤,引用失敗2025-07-07

