一 名詞解釋
1.WebSocket
WebSocket 是發送和接收消息的底層API,WebSocket 協議提供了通過一個套接字實現全雙工通信的功能。也能夠實現 web 瀏覽器和 server 間的異步通信,全雙工意味着 server 與瀏覽器間可以發送和接收消息。需要注意的是必須考慮瀏覽器是否支持
2.SockJs
為了應對許多瀏覽器不支持WebSocket協議的問題,設計了備選SockJs。
SockJS 是 WebSocket 技術的一種模擬。SockJS 會 盡可能對應 WebSocket API,但如果 WebSocket 技術不可用的話,就會選擇另外的通信方式協議。
3.STOMP
SockJS 為 WebSocket 提供了 備選方案。但無論哪種場景,對於實際應用來說,這種通信形式層級過低。下面看一下如何 在 WebSocket 之上使用 STOMP協議,來為瀏覽器 和 server 間的 通信增加適當的消息語義。(STOMP—— Simple Text Oriented Message Protocol——面向消息的簡單文本協議)
4.三者之間的關系
WebSocket 是底層協議,SockJS 是WebSocket 的備選方案,也是 底層協議,而 STOMP 是基於 WebSocket(SockJS) 的上層協議
二 springboot的config
<!-- websocket-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-websocket</artifactId>
</dependency>
1.基礎配置
@Configuration
@EnableWebSocketMessageBroker
public class WebSocketConfiguration implements WebSocketMessageBrokerConfigurer {
@Override
public void configureMessageBroker(MessageBrokerRegistry registry) {
registry.enableSimpleBroker("/topic", "/queue", "/user"); //表示在topic和queue這兩個域上服務端可以向客戶端發消息
registry.setApplicationDestinationPrefixes("/app");//客戶端向服務器端發送時的主題上面需要加"/app"作為前綴
registry.setUserDestinationPrefix("/user");//指定用戶發送一對一的主題,前綴是"/user"
}
@Override
public void registerStompEndpoints(StompEndpointRegistry registry) {
registry.addEndpoint("/stomp/endpoint").withSockJS();
}
}
第一步就是添加注解 此注解 這個配置類不僅配置了 WebSocket,還配置了基於代理的 STOMP 消息
第二步 配置一個消息代理..一個是允許服務器向客戶端發送的域 一個是客戶端想服務器發送的前綴,最后一個是指定一對第一發送的前綴
第三步 就是配置了節點,並且使用sockjs的方式
2.監聽連接與斷開
用到了redis管理session
@Access(name = "監聽websocket連接成功")
@EventListener
public void onConnectEvent(SessionConnectEvent event) {
//獲取Session連接信息
StompHeaderAccessor sha = StompHeaderAccessor.wrap(event.getMessage());
//獲取SessionId
String sessionId = sha.getSessionId();
//存儲redis
Map<String, String> map = new HashMap<String, String>();
map.put(sessionId, agentNo);
redisTemplate.opsForHash().putAll("webSocket", map);
}
@Access(name = "監聽websocket斷開連接")
@EventListener
public void onDisconnectEvent(SessionDisconnectEvent event) {
//獲取Session連接信息
StompHeaderAccessor sha = StompHeaderAccessor.wrap(event.getMessage());
//獲取SessionId
String sessionId = sha.getSessionId();
//redis刪除此sessionId
redisTemplate.opsForHash().delete("webSocket", sessionId);
}
}
三.實現前端代碼
1.引入js
<!-- websocket ****代表自己的項目路徑 這個寫自己的 --> <script src="/***/sockjs-client/sockjs.min.js"></script> <script src="/***/stomp-websocket/stomp.min.js"></script> <!-- 參考 --> <!-- https://cdn.bootcdn.net/ajax/libs/sockjs-client/1.4.0/sockjs.min.js https://cdn.bootcdn.net/ajax/libs/stomp.js/2.3.3/stomp.min.js -->
2.連接和訂閱消息
使用了agentNo 區分訂閱廣播消息與個人消息
var stompClient = null;
var socket = null;
function connect() {
//從sesionStorage獲取坐席工號 下面連接頭需要 這個按照自己需求修改
agentNo = sessionStorage.getItem("agentNo");
//建立連接對象(還未發起連接) 地址更換成自己的 連接服務器注冊端點endPoint時,寫訪問服務器的全路徑URL:
socket = new SockJS('地址');
//獲取 STOMP 子協議的客戶端對象
stompClient = Stomp.over(socket);
//建立連接,並且連接頭上定義了坐席工號
stompClient.connect({
userName: agentNo // <---這是頭 在后面的監聽 連接成功的類可以獲取到 業務代碼需要 按需刪除
}, function (frame) {
console.log('Connected: ' + frame);
//訂閱廣播消息
stompClient.subscribe('/topic/greetings', function (greeting) {
var data = JSON.parse(greeting.body).data;
//實現自己的需求
});
//訂閱個人消息
stompClient.subscribe('/user/' + agentNo + '/message', function (greeting) {
var data = JSON.parse(greeting.body);
//實現自己的需求
});
});
}
2.發送消息
function send(data) {
var param = {
id: data,
release: true
}
stompClient.send("/app/release", {}, JSON.stringify(param));
// <----- app 就是在config中定義的客戶端往服務器發送的前綴,param是信息.必須用實體發送后台必須用實體接收
//release是后面controller中定義的 相當於RequestMapping中寫的地址一樣
}
四.實現后端代碼
1.發送廣播
@Access(name = "發布公告")
@MessageMapping("/release")
@SendTo("/topic/greetings")
public WebResult release(@RequestBody Announcement announcement) {
//執行業務代碼邏輯
}
2.發送個人消息
@Autowired
private SimpMessagingTemplate messagingTemplate;
public void demo() {
// messagingTemplate.convertAndSend(url,""); //方法1
//方法2 相當於/user/' + agentNo + '/message' 對應前端訂閱的地址
messagingTemplate.convertAndSendToUser(ledgerQuery.getAgentNo(), "/message", "");
}
