springboot項(xiàng)目跟JavaScript簡(jiǎn)單websocket使用方式
websocket 簡(jiǎn)紹
WebSocket 是一種在單個(gè) TCP 連接上進(jìn)行全雙工通信的協(xié)議。WebSocket 使得客戶端和服務(wù)器之間的數(shù)據(jù)交換變得更加簡(jiǎn)單,允許服務(wù)端主動(dòng)向客戶端推送數(shù)據(jù)。
在 WebSocket API 中,瀏覽器和服務(wù)器只需要完成一次握手,兩者之間就直接可以創(chuàng)建持久性的連接,并進(jìn)行雙向數(shù)據(jù)傳輸。
WebSocket 的優(yōu)勢(shì)包括
- 全雙工通信:允許服務(wù)器主動(dòng)向客戶端發(fā)送信息,同時(shí)也能夠提供客戶端到服務(wù)器的低延遲通信。
- 減少帶寬消耗:一旦建立連接之后,通信開銷很小,因?yàn)椴恍枰狧TTP頭部。
創(chuàng)建更豐富的交互體驗(yàn):實(shí)時(shí)更新數(shù)據(jù)的能力,使得應(yīng)用能夠更加流暢地響應(yīng)用戶的動(dòng)作
可以通過(guò) JavaScript 的 WebSocket 對(duì)象來(lái)創(chuàng)建一個(gè) WebSocket 連接。
var ws = new WebSocket('ws://localhost:8080');
JavaScript 設(shè)置
處理事件
WebSocket 對(duì)象提供了幾個(gè)事件處理器來(lái)處理連接的狀態(tài)變化:
- open:連接成功建立時(shí)觸發(fā)。
- message:接收到消息時(shí)觸發(fā)。
- error:發(fā)生錯(cuò)誤時(shí)觸發(fā)。
- close:連接關(guān)閉時(shí)觸發(fā)。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>WebSocket Example</title>
<script>
document.addEventListener('DOMContentLoaded', function () {
const socket = new WebSocket('ws://localhost:8080/websocket');
socket.onopen = function (event) {
console.log('WebSocket connection established.');
};
socket.onmessage = function (event) {
console.log('Received from server:', event.data);
};
socket.onclose = function (event) {
console.log('WebSocket connection closed.');
};
socket.onerror = function (error) {
console.error('WebSocket error:', error);
};
function sendMessage() {
let message = document.getElementById('message').value;
socket.send(message);
console.log('Sent message:', message);
}
});
</script>
</head>
<body>
<input type="text" id="message" placeholder="Type your message here">
<button onclick="sendMessage()">Send Message</button>
</body>
</html>
確保你的服務(wù)器端已正確配置,并且能夠接收來(lái)自客戶端的連接請(qǐng)求。
使用HTTPS和WSS(加密的WebSocket)可以提高安全性,特別是在生產(chǎn)環(huán)境中。
如果你的服務(wù)器部署在不同的域上,請(qǐng)確保服務(wù)器配置允許跨域訪問(wèn)。
Java 服務(wù)端設(shè)置
導(dǎo)jar包
通過(guò) maven 導(dǎo)入 spring boot 中 集成的 websocket jar 包
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-websocket</artifactId>
</dependency>
創(chuàng)建WebSocket端點(diǎn)
@EnableWebSocket
@EnableWebSocket 是 Spring 框架中的一個(gè)注解,用于啟用 WebSocket 支持。當(dāng)一個(gè)類被標(biāo)記為此注解時(shí),Spring 會(huì)自動(dòng)配置必要的組件來(lái)處理 WebSocket 請(qǐng)求。這個(gè)注解通常應(yīng)用在一個(gè)配置類上,該類負(fù)責(zé)設(shè)置 WebSocket 的處理邏輯。
registerWebSocketHandlers
- registerWebSocketHandlers 是 Spring WebSocket 框架中的一個(gè)重要方法,它用來(lái)注冊(cè) WebSocket 處理器(WebSocketHandler)。這個(gè)方法是 WebSocketConfigurer 接口中定義的一個(gè)抽象方法,需要在你的配置類中實(shí)現(xiàn)。通過(guò)這個(gè)方法,你可以指定哪些路徑映射到特定的 WebSocket 處理器,并可以配置其他相關(guān)選項(xiàng)。
- addHandler 方法:接受兩個(gè)參數(shù),一個(gè)是 WebSocketHandler 的實(shí)例,另一個(gè)是 WebSocket 終結(jié)點(diǎn)的 URL 映射。
- setAllowedOrigins 方法:指定哪些源(域名)可以訪問(wèn)你的 WebSocket 終結(jié)點(diǎn)。如果設(shè)置為 “*”,則表示任何源都可以訪問(wèn)。
- withSockJS 方法:這是一個(gè)可選的配置項(xiàng),用于啟用 SockJS,它是一種兼容性更好的 WebSocket 實(shí)現(xiàn),能夠在不支持原生 WebSocket 的瀏覽器上使用其他傳輸方式(如長(zhǎng)輪詢)。
接下來(lái),定義一個(gè)WebSocket處理器類,并配置一個(gè)端點(diǎn)來(lái)接受WebSocket連接:
import org.springframework.context.annotation.Configuration;
import org.springframework.web.socket.config.annotation.EnableWebSocket;
import org.springframework.web.socket.config.annotation.WebSocketConfigurer;
import org.springframework.web.socket.config.annotation.WebSocketHandlerRegistry;
@Configuration
@EnableWebSocket
public class WebSocketConfig implements WebSocketConfigurer {
private final WebSocketHandler webSocketHandler;
public WebSocketConfig(WebSocketHandler webSocketHandler) {
this.webSocketHandler = webSocketHandler;
}
@Override
public void registerWebSocketHandlers(WebSocketHandlerRegistry registry) {
registry.addHandler(webSocketHandler, "/websocket").setAllowedOrigins("*");
}
}
這里我們定義了一個(gè)WebSocket配置類,并注冊(cè)了WebSocket處理器。/websocket 是WebSocket的端點(diǎn)路徑。
實(shí)現(xiàn)WebSocket處理器
afterConnectionEstablished
void afterConnectionEstablished(WebSocketSession session) throws Exception;
- afterConnectionEstablished 是 WebSocketHandler 接口中定義的一個(gè)方法,它會(huì)在 WebSocket 連接成功建立之后被調(diào)用。這個(gè)方法為你提供了一個(gè)機(jī)會(huì)來(lái)執(zhí)行一些初始化操作,例如記錄連接信息、將連接加入到一個(gè)管理連接的集合中,或者發(fā)送初始消息給客戶端。
- WebSocketSession session —— 表示新建立的 WebSocket 會(huì)話。
- Exception —— 如果處理過(guò)程中出現(xiàn)任何異常,可以拋出
afterConnectionClosed
void afterConnectionClosed(WebSocketSession session, CloseStatus status) throws Exception;
- afterConnectionClosed 是 WebSocketHandler 接口中定義的一個(gè)方法,它會(huì)在 WebSocket 連接關(guān)閉時(shí)被調(diào)用。這個(gè)方法允許你在連接關(guān)閉后執(zhí)行一些清理操作,例如從會(huì)話管理器中移除會(huì)話、記錄關(guān)閉事件或者發(fā)送通知給其他客戶端。
- WebSocketSession session:表示已關(guān)閉的 WebSocket 會(huì)話。
- CloseStatus status:表示關(guān)閉狀態(tài)的原因。CloseStatus 是一個(gè)枚舉類型,提供了標(biāo)準(zhǔn)的關(guān)閉碼和關(guān)閉原因,例如 NORMAL_CLOSURE(正常關(guān)閉)、GOING_AWAY(離開)、PROTOCOL_ERROR(協(xié)議錯(cuò)誤)等。
- 拋出:Exception —— 如果處理過(guò)程中出現(xiàn)任何異常,可以拋出。
handleTextMessage
void handleTextMessage(WebSocketSession session, TextMessage message) throws Exception;
- handleTextMessage 是 WebSocketHandler 接口中定義的一個(gè)方法,用于處理從客戶端接收到的文本消息。當(dāng)客戶端發(fā)送文本消息時(shí),這個(gè)方法會(huì)被調(diào)用。對(duì)于二進(jìn)制消息,則會(huì)調(diào)用 handleBinaryMessage 方法。
- WebSocketSession session:表示當(dāng)前的 WebSocket 會(huì)話。
- TextMessage message:包含從客戶端接收到的文本消息。
- 拋出:Exception —— 如果處理過(guò)程中出現(xiàn)任何異常,可以拋出
- 現(xiàn)在我們需要實(shí)現(xiàn)一個(gè)WebSocket處理器類來(lái)處理連接事件:
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.util.concurrent.ConcurrentHashMap;
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.util.concurrent.ConcurrentHashMap;
public class WebSocketHandler extends TextWebSocketHandler {
private static final ConcurrentHashMap<String, WebSocketSession> sessions = new ConcurrentHashMap<>();
@Override
public void afterConnectionEstablished(WebSocketSession session) throws Exception {
super.afterConnectionEstablished(session);
String id = session.getId();
sessions.put(id, session);
System.out.println("WebSocket connection established with session ID: " + id);
}
@Override
public void afterConnectionClosed(WebSocketSession session, CloseStatus status) throws Exception {
super.afterConnectionClosed(session, status);
String id = session.getId();
sessions.remove(id);
System.out.println("WebSocket connection closed with session ID: " + id);
}
public void sendMessageToSession(String sessionId, String message) {
WebSocketSession session = sessions.get(sessionId);
if (session != null && session.isOpen()) {
try {
session.sendMessage(new TextMessage(message));
} catch (Exception e) {
e.printStackTrace();
}
}
}
public void broadcastMessage(String message) {
for (WebSocketSession session : sessions.values()) {
if (session.isOpen()) {
try {
session.sendMessage(new TextMessage(message));
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
}
現(xiàn)在我們可以使用WebSocketHandler類中的sendMessageToSession或broadcastMessage方法來(lái)發(fā)送數(shù)據(jù)給前端。
發(fā)送給特定會(huì)話:如果想要向特定的客戶端發(fā)送消息,可以使用sendMessageToSession方法。你需要知道客戶端的會(huì)話ID。
webSocketHandler.sendMessageToSession(sessionId, "Hello, client!");
廣播消息:如果你想向所有已連接的客戶端廣播消息,可以使用broadcastMessage方法
webSocketHandler.broadcastMessage("Hello, everyone!")
注銷WebSocket連接
要在Java后端注銷WebSocket連接,可以通過(guò)調(diào)用 WebSocketHandler 類中的 removeSession 方法來(lái)實(shí)現(xiàn)。例如,你可以從HTTP請(qǐng)求或其他服務(wù)中調(diào)用此方法:
@GetMapping("/logout/{sessionId}")
public ResponseEntity<String> logout(@PathVariable String sessionId) {
WebSocketHandler handler = new WebSocketHandler(); // 或者通過(guò)依賴注入獲取
handler.removeSession(sessionId);
return ResponseEntity.ok("Session " + sessionId + " has been closed.");
}
自己定義一個(gè) 固定id 方便使用
前端 JavaScript 代碼
- 創(chuàng)建WebSocket連接:在DOM加載完成后,創(chuàng)建一個(gè)WebSocket連接。
- 發(fā)送握手消息:在連接建立后,發(fā)送一個(gè)包含固定ID的握手消息。
- 處理接收到的消息:設(shè)置消息接收處理函數(shù),用于接收后端發(fā)送的數(shù)據(jù)。
后端 Java 代碼
- 配置WebSocket處理器:配置WebSocket處理器,注冊(cè)WebSocket路徑。
- 處理握手消息:接收前端發(fā)送的握手消息,并根據(jù)消息內(nèi)容保存客戶端ID與WebSocket會(huì)話的映射。
- 發(fā)送消息:提供方法根據(jù)客戶端ID發(fā)送消息。
前端 javascript
<!DOCTYPE html>
<html l ang="en">
<head>
<meta charset="UTF-8">
<title>WebSocket Client Example</title>
</head>
<body>
<input type="text" id="messageInput" placeholder="Enter your message here">
<button id="sendButton">Send Message</button>
<div id="log"></div>
<script>
document.addEventListener('DOMContentLoaded', function () {
const socket = new WebSocket('ws://example.com/websocket');
socket.onopen = function (event) {
console.log('WebSocket connection established.');
// 固定的客戶端ID
const clientId = 'client-123';
// 發(fā)送握手消息
socket.send(JSON.stringify({
type: 'handshake',
clientId: clientId,
message: 'Hello, server!'
}));
};
socket.onmessage = function (event) {
console.log('Received from server:', event.data);
document.getElementById('log').textContent += 'Received from server: ' + event.data + '\n';
};
socket.onclose = function (event) {
console.log('WebSocket connection closed.');
document.getElementById('log').textContent += 'WebSocket connection closed.\n';
};
socket.onerror = function (error) {
console.error('WebSocket error:', error);
document.getElementById('log').textContent += 'WebSocket error: ' + error + '\n';
};
document.getElementById('sendButton').addEventListener('click', function () {
let message = document.getElementById('messageInput').value;
if (socket.readyState === WebSocket.OPEN) {
socket.send(message);
document.getElementById('log').textContent += 'Sent message: ' + message + '\n';
} else {
alert('WebSocket connection is not open.');
}
});
});
</script>
</body>
</html>
java 服務(wù)器端配置
import com.fasterxml.jackson.databind.ObjectMapper;
import com.ruoyi.common.utils.socket.HandshakeMessage;
import org.springframework.stereotype.Component;
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.util.Map;
import java.util.concurrent.ConcurrentHashMap;
@Component
public class WebSocketHandler extends TextWebSocketHandler {
private static final Map<String, WebSocketSession> clientSessions = new ConcurrentHashMap<>();
private static final ObjectMapper objectMapper = new ObjectMapper();
@Override
public void afterConnectionEstablished(WebSocketSession session) throws Exception {
super.afterConnectionEstablished(session);
String sessionId = session.getId();
System.out.println("WebSocket connection established with session ID: " + sessionId);
}
@Override
protected void handleTextMessage(WebSocketSession session, TextMessage message) throws Exception {
String payload = message.getPayload();
HandshakeMessage handshakeMessage = objectMapper.readValue(payload, HandshakeMessage.class);
if ("handshake".equals(handshakeMessage.getType())) {
String clientId = handshakeMessage.getClientId();
String sessionId = session.getId();
// 存儲(chǔ)clientId與sessionId的映射關(guān)系
clientSessions.put(clientId, session);
// 可以選擇回復(fù)客戶端確認(rèn)握手成功的消息
session.sendMessage(new TextMessage("Handshake successful"));
}
}
@Override
public void afterConnectionClosed(WebSocketSession session, CloseStatus status) throws Exception {
super.afterConnectionClosed(session, status);
String sessionId = session.getId();
System.out.println("WebSocket connection closed with session ID: " + sessionId);
// 移除會(huì)話
clientSessions.values().removeIf(s -> s.getId().equals(sessionId));
}
public void sendMessageToClient(String clientId, String message) {
WebSocketSession session = clientSessions.get(clientId);
if (session != null && session.isOpen()) {
try {
session.sendMessage(new TextMessage(message));
} catch (Exception e) {
e.printStackTrace();
}
}
}
// 定義握手消息模型
private static class HandshakeMessage {
private String type;
private String clientId;
private String message;
// Getters and setters
}
}
總結(jié)
以上為個(gè)人經(jīng)驗(yàn),希望能給大家一個(gè)參考,也希望大家多多支持腳本之家。
- SpringBoot使用JSONPath實(shí)現(xiàn)高效處理JSON數(shù)據(jù)
- SpringBoot同時(shí)接收文件以及json參數(shù)實(shí)現(xiàn)方式
- Mybatis 的 Interceptor(攔截器) 與 JSqlparser 結(jié)合解析SQL 使SpringBoot項(xiàng)目多數(shù)據(jù)庫(kù)兼容的嘗試(推薦)
- SpringBoot整合JSP的實(shí)現(xiàn)示例
- springboot加載json配置全過(guò)程
- 前端vue2+js+springboot實(shí)現(xiàn)excle導(dǎo)入優(yōu)化解決方案
相關(guān)文章
springmvc字符編碼過(guò)濾器CharacterEncodingFilter的使用
這篇文章主要介紹了springmvc字符編碼過(guò)濾器CharacterEncodingFilter的使用,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。2021-08-08
K均值聚類算法的Java版實(shí)現(xiàn)代碼示例
這篇文章主要介紹了K均值聚類算法的Java版實(shí)現(xiàn)代碼示例,具有一定借鑒價(jià)值,需要的朋友可以參考下。2017-12-12
Spring?Boot整合Zookeeper實(shí)現(xiàn)分布式鎖的場(chǎng)景分析
這篇文章主要介紹了Spring?Boot整合Zookeeper實(shí)現(xiàn)分布式鎖,zk實(shí)現(xiàn)分布式鎖完全是依靠zk節(jié)點(diǎn)類型當(dāng)中的臨時(shí)序號(hào)節(jié)點(diǎn)來(lái)實(shí)現(xiàn)的,本文通過(guò)實(shí)例代碼給大家介紹的非常詳細(xì),需要的朋友可以參考下2022-06-06
springboot 使用poi進(jìn)行數(shù)據(jù)的導(dǎo)出過(guò)程詳解
這篇文章主要介紹了springboot 使用poi進(jìn)行數(shù)據(jù)的導(dǎo)出過(guò)程詳解,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2019-09-09
詳解JFX11+IDEA跨平臺(tái)打包發(fā)布的完美解決辦法
這篇文章主要介紹了詳解JFX11+IDEA跨平臺(tái)打包發(fā)布的完美解決辦法,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2020-06-06

