SpringBoot整合WebSocket實現(xiàn)聊天室流程全解
什么是WebSocket
WebSocket是一種在單個TCP連接上進行全雙工通信的協(xié)議。WebSocket使得客戶端和服務器之間的數(shù)據(jù)交換變得更加簡單,允許服務端主動向客戶端推送數(shù)據(jù)。在WebSocket API中,瀏覽器和服務器只需要完成一次握手,兩者之間就直接可以創(chuàng)建持久性的連接,并進行雙向數(shù)據(jù)傳輸。
WebSocket通信模型

為什么需要WebSocket
http 通信是單向的,發(fā)送請求獲取響應,沒有請求也就沒有響應。
簡單理解:
- HTTP 打電話:客戶端問一句服務端答一句
- WebSocket 打電話:雙向對話

Websocket與http的關系

相同點:
- 都是基于tcp的,都是可靠性傳輸協(xié)議
- 都是應用層協(xié)議
不同點:
- WebSocket是雙向通信協(xié)議,模擬Socket協(xié)議,可以雙向發(fā)送或接受信息
- HTTP是單向的
- WebSocket是需要瀏覽器和服務器握手進行建立連接的而http是瀏覽器發(fā)起向服務器的連接,服務器預先并不知道這個連接。
SpringBoot集成WebSocket
maven依賴
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-websocket</artifactId>
</dependency>
WebSocketConfig
@Configuration
public class WebSocketConfig {
@Bean
public ServerEndpointExporter serverEndpointExporter() {
return new ServerEndpointExporter();
}
}
WebSocketService
@Slf4j
@Service
@ServerEndpoint(value = "/myService/{userId}")
public class WebSocketService {
/**
* 靜態(tài)變量,用來記錄當前在線連接數(shù)。應該把它設計成線程安全的。
*/
private static AtomicInteger onlineCount = new AtomicInteger(0);;
/**
* concurrent包的線程安全Set,用來存放每個客戶端對應的MyWebSocket對象。
*/
private static ConcurrentHashMap<String,WebSocketService> webSocketMap = new ConcurrentHashMap<>();
/**
* concurrent包的線程安全Set,用來存放每個客戶端對應的session對象。
*/
private static ConcurrentHashMap<String,Session> sessionMap = new ConcurrentHashMap<>();
/**
* 與某個客戶端的連接會話,需要通過它來給客戶端發(fā)送數(shù)據(jù)
*/
private Session session;
private String userId = "";
/**
* 連接建立成功調用的方法
*/
@OnOpen
public void onOpen(Session session,@PathParam("userId") String userId) {
this.session = session;
this.userId = userId;
if(webSocketMap.containsKey(userId) && sessionMap.containsKey(userId)){
webSocketMap.remove(userId);
sessionMap.remove(userId);
sessionMap.put(userId,session);
webSocketMap.put(userId,this);
}else{
webSocketMap.put(userId,this);
sessionMap.put(userId,session);
addOnlineCount();
}
log.info("用戶連接:"+userId+",當前在線人數(shù)為:" + getOnlineCount());
}
/**
* 連接關閉調用的方法
*/
@OnClose
public void onClose() {
if(webSocketMap.containsKey(userId)){
webSocketMap.remove(userId);
subOnlineCount();
}
log.info("用戶退出:"+userId+",當前在線人數(shù)為:" + getOnlineCount());
}
/**
* 收到客戶端消息后調用的方法
*/
@OnMessage
public void onMessage(String message, Session session) {
this.session = session;
log.info("收到客戶端消息 -> {}",message);
//服務端收到客戶端的消息并推送給客戶端
sendMessage(message);
}
/**
* 發(fā)生錯誤時調用
*/
@OnError
public void onError(Session session, Throwable error) {
log.error(error.getMessage());
}
/**
* 實現(xiàn)服務器主動推送 可以通過controller調用此方法實現(xiàn)主動推送
*/
public void sendMessage(String message){
try {
Set<Map.Entry<String, Session>> entries = sessionMap.entrySet();
for (Map.Entry<String, Session> next : entries) {
Session session = next.getValue();
session.getBasicRemote().sendText(this.userId + "說" + message);
}
} catch (IOException e) {
log.error(e.getMessage());
}
}
public static synchronized int getOnlineCount() {
return onlineCount.get();
}
public static synchronized void addOnlineCount() {
WebSocketService.onlineCount.getAndIncrement();
}
public static synchronized void subOnlineCount() {
WebSocketService.onlineCount.getAndDecrement();
}
}WebSocket.html
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>websocket通訊</title>
</head>
<body>
<p>userId:<input id="userId" name="userId" type="text" value="10"></p>
<p>msg:<input id="contentText" name="contentText" type="text" value="hello websocket"></p>
<p>操作:<button onclick="openSocket()">開啟socket</button></p>
<p>操作:<button onclick="sendMessage()">發(fā)送消息</button></p>
</body>
<script type="application/javascript">
let socket;
function openSocket() {
if(socket != null){
socket.close();
socket = null;
}
let userId = document.getElementById('userId').value
socket = new WebSocket("ws://localhost:9000/myService/"+userId);
//打開事件
socket.onopen = function() {
console.log("websocket已打開");
};
//獲得消息事件
socket.onmessage = function(msg) {
console.log(msg.data);
};
//關閉事件
socket.onclose = function() {
console.log("websocket已關閉");
};
//發(fā)生了錯誤事件
socket.onerror = function() {
console.log("websocket發(fā)生了錯誤");
}
}
function sendMessage() {
let contentText = document.getElementById('contentText').value
socket.send(contentText);
}
</script>
</html>測試

到此這篇關于SpringBoot整合WebSocket實現(xiàn)聊天室流程全解的文章就介紹到這了,更多相關SpringBoot WebSocket聊天室內容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家!
相關文章
Spring-IOC容器-Bean管理-基于XML方式超詳解
這篇文章主要介紹了Spring為IOC容器Bean的管理,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友可以參考下2021-08-08
ThreadPoolExecutor參數(shù)的用法及說明
這篇文章主要介紹了ThreadPoolExecutor參數(shù)的用法及說明,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2023-03-03

