PS:帶有自己一點個人的理解
1.什麼是websocket,為什麼要去使用它
首先看一下[維基百科上對Websocket的定義](WebSocket - 維基百科,自由的百科全書 (wikipedia.org)),WebSocket是一種網路傳輸協議,可在單個TCP連線上進行全雙工通訊,位於OSI模型應用層應用層。
透過這個定義,其實就已經能夠看出websocket的重要性了。我們一般對前後端進行互動採用http協議,但http是一種無狀態的、無連線的、單向的應用層協議。因此對於C/S架構,客戶端只能向服務端傳送請求之後服務端才能向客戶端傳送資料,服務端屬於被動的一方,這使得在一些高實時性要求的業務中,http要反覆連線甚至不關閉,而且速度也有些慢了。
Websocket作為全雙工通訊的協議,中間的傳輸通道相當自由,不管是服務端還是客戶端都能夠自由傳送訊息,速度上便能夠大大提升。
2.簡單展示一下我使用Websocket的用例
前端js和後端springboot作為演示物件,這是我自己的監控作業系統資訊的一個小專案,我採用的是Websocket的原生註解。
以下是部分程式碼演示。
首先要建立一個後端的Websocket伺服器端點
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.stereotype.Component;
import org.springframework.web.socket.server.standard.ServerEndpointExporter;
@Configuration
@Component
public class WebSocketConfig {
@Bean
public ServerEndpointExporter serverEndpointExporter() {
return new ServerEndpointExporter();
}
}
然後是後端伺服器的配置和相關業務的處理
import com.example.cypherserverside.service.collect.SysDataServer;
import jakarta.websocket.*;
import jakarta.websocket.server.ServerEndpoint;
import lombok.NonNull;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.stereotype.Component;
import java.util.concurrent.*;
@Slf4j
@ServerEndpoint(value = "/ws/url")
@Component
public class WebSocketServer implements ApplicationContextAware {
//執行緒安全的資料結構,防止執行緒衝突,Session就是和前端建立連線後由DI容器自動建立的
private final static ConcurrentHashMap<String, Session> sessions = new ConcurrentHashMap<>();
private final static ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(1);
//為了獲取Bean的欄位
private static ApplicationContext _applicationContext;
private static SysDataServer sysDataServer;
@Override
public void setApplicationContext(@NonNull ApplicationContext applicationContext) throws BeansException {
_applicationContext = applicationContext;
//啟動定時任務
startScheduledTask();
}
/**
* 開始連線
* @param session 客戶端傳來的session
*/
@OnOpen
public void onOpen(Session session) {
sessions.put(session.getId(), session);
session.setMaxIdleTimeout(30000);
System.out.println("連線:" + session.getId());
}
/**
* 收到客戶端訊息
* @param message 來自前端的訊息
*/
@OnMessage
public void onMessage(String message) {
log.info("onMessage: " + message);
}
/**
* 斷開連線
*/
@OnClose
public void onClose(Session session) {
for (String key : sessions.keySet()) {
if (key.equals(session.getId())) {
sessions.remove(key);
System.out.println("關閉" + session.getId());
}
}
}
/**
* 連線失敗出現異常
* @param t 異常
*/
@OnError
public void onError(Throwable t) {
log.error(t.getMessage(), t);
}
/**
* 定時任務啟動,兩秒一傳送
*/
private static void startScheduledTask(){
scheduler.scheduleAtFixedRate(()->{
sysDataServer = _applicationContext.getBean(SysDataServer.class);
//我自己的業務邏輯,獲取JsonString資料然後發給前端
String systemMessage = sysDataServer.toJSONString(sysDataServer.collectData());
broadcast(systemMessage);
}, 0, 2, TimeUnit.SECONDS);
}
//傳送資料
private static void broadcast(String message){
for (Session session : sessions.values()) {
session.getAsyncRemote().sendText(message);
System.out.println("已傳送:" + session.getId());
}
}
}
js部分程式碼展示
import { ref } from 'vue';
const processes = ref([]);
const ws = new WebSocket('ws://localhost:8080/ws/url');
ws.onopen = () => {
console.log('WebSocket 連線成功');
socket.send("Hello, Server!");
};
ws.onmessage = (event) => {
const data = JSON.parse(event.data);
// 處理後端資料
processes.value = data.map(process => ({
pid: process.pid,
pname: process.pName,
cpuUsage: process.processCpuUsage,
memoryUsage: process.processMemoryUsage
}));
};
ws.onclose = () => {
console.log('WebSocket 連線關閉');
};