springBoot整合webSocket並使用postMan進行測試
參考資料:
https://blog.csdn.net/qq_54773998/article/details/123863493
https://blog.csdn.net/weixin_56995925/article/details/120543965
簡單描述
簡單來講,webSocket是一種在http協議基礎上的另一種新協議,叫ws協議。
http協議是單工通訊,客戶端發起請求,服務端收到請求並處理,返回給客戶端,然後客戶端收到服務端的請求。
ws協議是全雙工通訊,客戶端發起請求後,相當於搭建了一個通道,在不斷開的情況下,在這期間,服務端可以把請求發給客戶端,客戶端也可以在這期間處理別的事情,不必等待服務端的響應。
如果不理解,可參考這篇文章:https://blog.csdn.net/qq_54773998/article/details/123863493
webSockt實現
此次webSocket實現不包含前端程式碼,將使用postMan來實現前端的功能。
依賴
xml
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-websocket</artifactId>
</dependency>
配置類
```java @Configuration public class WebSocketConfig {
@Bean
public ServerEndpointExporter serverEndpointExporter(){
return new ServerEndpointExporter();
}
} ```
webSocketServer
```java package com.czf.study.wevSocket;
import lombok.extern.slf4j.Slf4j; import org.junit.platform.commons.util.StringUtils; import org.springframework.stereotype.Component;
import javax.websocket.*; import javax.websocket.server.PathParam; import javax.websocket.server.ServerEndpoint; import java.io.IOException; import java.util.concurrent.ConcurrentHashMap;
/
* @author zfChen
* @create 2022/11/14 15:11
*/
@ServerEndpoint("/websocket/{userId}")
@Component
@Slf4j
public class WebSocketServer {
/靜態變數,用來記錄當前線上連線數。應該把它設計成執行緒安全的。/
private static int onlineCount = 0;
/concurrent包的執行緒安全集合,也可以map改成set,用來存放每個客戶端對應的MyWebSocket物件。/
private static ConcurrentHashMap
/**
* 連線建立成功呼叫的方法*/
@OnOpen
public void onOpen(Session session,@PathParam("userId") String userId) {
this.session = session;
this.userId=userId;
if(!webSocketMap.containsKey(userId)){
//加入集合中
webSocketMap.put(userId,this);
//線上數加1
addOnlineCount();
}
log.info("使用者連線:"+userId+",當前線上人數為:" + getOnlineCount());
try {
sendMessage("連線成功");
} catch (IOException e) {
log.error("使用者:"+userId+",網路異常!!!!!!");
}
}
/**
* 連線關閉呼叫的方法
*/
@OnClose
public void onClose() {
if(webSocketMap.containsKey(userId)){
webSocketMap.remove(userId);
//從集合中刪除
subOnlineCount();
}
log.info("使用者退出:"+userId+",當前線上人數為:" + getOnlineCount());
}
/**
* 收到客戶端訊息後呼叫的方法
*
* @param message 客戶端傳送過來的訊息*/
@OnMessage
public void onMessage(String message, Session session) {
log.info("【websocket訊息】收到客戶端發來的訊息:{}", message);
}
/**
*
* @param session
* @param error
*/
@OnError
public void onError(Session session, Throwable error) {
log.error("使用者錯誤:"+this.userId+",原因:"+error.getMessage());
error.printStackTrace();
}
/**
* 實現伺服器主動推送
*/
public void sendMessage(String message) throws IOException {
this.session.getBasicRemote().sendText(message);
}
/**
* 傳送自定義訊息
* */
public static void sendInfo(String message,@PathParam("userId") String userId) throws IOException {
log.info("傳送訊息到:"+userId+",報文:"+message);
if(StringUtils.isNotBlank(userId)&&webSocketMap.containsKey(userId)){
webSocketMap.get(userId).sendMessage(message);
}else{
log.error("使用者"+userId+",不線上!");
}
}
public static synchronized int getOnlineCount() {
return onlineCount;
}
public static synchronized void addOnlineCount() {
WebSocketServer.onlineCount++;
}
public static synchronized void subOnlineCount() {
WebSocketServer.onlineCount--;
}
}
```
服務端發請求介面
外面建立一個介面,模擬服務端發請求給客戶端
```java @RestController public class DemoController {
@RequestMapping("/push/{toUserId}")
public ResponseEntity<String> pushToWeb(String message, @PathVariable String toUserId) throws IOException {
WebSocketServer.sendInfo(message,toUserId);
return ResponseEntity.ok("MSG SEND SUCCESS");
}
} ```
測試
使用postMan建立webSocket請求
輸入webSocket的地址,1表示userId=1
此時控制檯輸出
2022-11-15 11:51:43.009 INFO 28972 --- [nio-8787-exec-5] com.czf.study.wevSocket.WebSocketServer : 使用者連線:1,當前線上人數為:1
接下來,模擬服務端給客戶端傳送請求,建立一個http請求
控制檯輸出
2022-11-15 11:53:48.235 INFO 28972 --- [nio-8787-exec-4] com.czf.study.wevSocket.WebSocketServer : 傳送訊息到:1,報文:hello
客戶端收到請求
雙人聊天室
webSocket經常被用作聊天室,兩個客戶端,通過一個服務端分發請求,進行溝通。
在此案例中,通過/
來區分,前一個是訊息,後一個是傳送的物件。
java
/**
* 收到客戶端訊息後呼叫的方法
*
* @param message 客戶端傳送過來的訊息*/
@OnMessage
public void onMessage(String message, Session session) {
log.info("【websocket訊息】收到客戶端發來的訊息:{}", message);
String[] split = message.split("/");
try {
sendInfo(split[0],split[1]);
} catch (IOException e) {
e.printStackTrace();
}
}
比如
2號使用者收到1號傳送的訊息
同樣的,2號也可以傳送訊息給1號。