Springboot整合websocket實(shí)現(xiàn)一對一消息推送和廣播消息推送
作者:kunm
segmentfault.com/a/1190000011908831
springboot基礎(chǔ)環(huán)境
請參考springboot文檔
maven依賴
????????<dependency>
????????????<groupId>org.springframework.bootgroupId>
????????????<artifactId>spring-boot-starter-websocketartifactId>
????????dependency>
代碼準(zhǔn)備
準(zhǔn)備常量類
????//webSocket相關(guān)配置
????//鏈接地址
????public?static?String?WEBSOCKETPATHPERFIX?=?"/ws-push";
????public?static?String?WEBSOCKETPATH?=?"/endpointWisely";
????//消息代理路徑
????public?static?String?WEBSOCKETBROADCASTPATH?=?"/topic";
????//前端發(fā)送給服務(wù)端請求地址
????public?static?final?String?FORETOSERVERPATH?=?"/welcome";
????//服務(wù)端生產(chǎn)地址,客戶端訂閱此地址以接收服務(wù)端生產(chǎn)的消息
????public?static?final?String?PRODUCERPATH?=?"/topic/getResponse";
????//點(diǎn)對點(diǎn)消息推送地址前綴
????public?static?final?String?P2PPUSHBASEPATH?=?"/user";
????//點(diǎn)對點(diǎn)消息推送地址后綴,最后的地址為/user/用戶識別碼/msg
????public?static?final?String?P2PPUSHPATH?=?"/msg";
接收前端消息實(shí)體
public?class?WiselyMessage?{
????private?String?name;
????public?String?getName()?{
????????return?name;
????}
????public?void?setName(String?name)?{
????????this.name?=?name;
????}
}
后臺發(fā)送消息實(shí)體
private?String?responseMessage;
????public?WiselyResponse(String?responseMessage){
????????this.responseMessage?=?responseMessage;
????}
????public?String?getResponseMessage()?{
????????return?responseMessage;
????}
????public?void?setResponseMessage(String?responseMessage)?{
????????this.responseMessage?=?responseMessage;
????}
配置websocket
@Configuration
//?@EnableWebSocketMessageBroker注解用于開啟使用STOMP協(xié)議來傳輸基于代理(MessageBroker)的消息,這時(shí)候控制器(controller)
//?開始支持@MessageMapping,就像是使用@requestMapping一樣。
@EnableWebSocketMessageBroker
public?class?WebSocketConfig?extends?AbstractWebSocketMessageBrokerConfigurer?{
????@Override
????public?void?registerStompEndpoints(StompEndpointRegistry?stompEndpointRegistry)?{
????????//注冊一個(gè)Stomp的節(jié)點(diǎn)(endpoint),并指定使用SockJS協(xié)議。
????????stompEndpointRegistry.addEndpoint(Constant.WEBSOCKETPATH).withSockJS();
????}
????@Override
????public?void?configureMessageBroker(MessageBrokerRegistry?registry)?{
????????//服務(wù)端發(fā)送消息給客戶端的域,多個(gè)用逗號隔開
????????registry.enableSimpleBroker(Constant.WEBSOCKETBROADCASTPATH,?Constant.P2PPUSHBASEPATH);
????????//定義一對一推送的時(shí)候前綴
????????registry.setUserDestinationPrefix(Constant.P2PPUSHBASEPATH);
????????//定義websoket前綴
????????registry.setApplicationDestinationPrefixes(Constant.WEBSOCKETPATHPERFIX);
????}
}
service
@Service
public?class?WebSocketService?{
????@Autowired
????private?SimpMessagingTemplate?template;
????/**
?????*?廣播
?????*?發(fā)給所有在線用戶
?????*
?????*?@param?msg
?????*/
????public?void?sendMsg(WiselyResponse?msg)?{
????????template.convertAndSend(Constant.PRODUCERPATH,?msg);
????}
????/**
?????*?發(fā)送給指定用戶
?????*?@param?users
?????*?@param?msg
?????*/
????public?void?send2Users(List?users,?WiselyResponse?msg) ?{
????????users.forEach(userName?->?{
????????????template.convertAndSendToUser(userName,?Constant.P2PPUSHPATH,?msg);
????????});
????}
}
控制器
@Controller
public?class?WsController?{
????@Resource
????WebSocketService?webSocketService;
????@MessageMapping(Constant.FORETOSERVERPATH)//@MessageMapping和@RequestMapping功能類似,用于設(shè)置URL映射地址,瀏覽器向服務(wù)器發(fā)起請求,需要通過該地址。
????@SendTo(Constant.PRODUCERPATH)//如果服務(wù)器接受到了消息,就會(huì)對訂閱了@SendTo括號中的地址傳送消息。
????public?WiselyResponse?say(WiselyMessage?message)?throws?Exception?{
????????List?users?=?Lists.newArrayList();
????????users.add("d892bf12bf7d11e793b69c5c8e6f60fb");//此處寫死只是為了方便測試,此值需要對應(yīng)頁面中訂閱個(gè)人消息的userId
????????webSocketService.send2Users(users,?new?WiselyResponse("admin?hello"));
????????return?new?WiselyResponse("Welcome,?"?+?message.getName()?+?"!");
????}
}
頁面
<html?xmlns:th="http://www.thymeleaf.org">
<head>
????<meta?charset="UTF-8"?/>
????<title>Spring?Boot+WebSocket+廣播式title>
head>
<body?onload="disconnect()">
<noscript><h2?style="color:?#ff0000">貌似你的瀏覽器不支持websocketh2>noscript>
<div>
????<div>
????????<button?id="connect"?onclick="connect();">連接button>
????????<button?id="disconnect"?disabled="disabled"?onclick="disconnect();">斷開連接button>
????div>
????<div?id="conversationDiv">
????????<label>輸入你的名字label><input?type="text"?id="name"?/>
????????<button?id="sendName"?onclick="sendName();">發(fā)送button>
????????<p?id="response">p>
????????<p?id="response1">p>
????div>
div>
<script?src="https://cdn.bootcss.com/sockjs-client/1.1.4/sockjs.min.js">script>
<script?src="https://cdn.bootcss.com/stomp.js/2.3.3/stomp.min.js">script>
<script?src="https://cdn.bootcss.com/jquery/3.2.1/jquery.min.js">script>
<script?th:inline="javascript">
????var?stompClient?=?null;
????//此值有服務(wù)端傳遞給前端,實(shí)現(xiàn)方式?jīng)]有要求
????var?userId?=?[[${userId}]];
????function?setConnected(connected)?{
????????document.getElementById('connect').disabled?=?connected;
????????document.getElementById('disconnect').disabled?=?!connected;
????????document.getElementById('conversationDiv').style.visibility?=?connected???'visible'?:?'hidden';
????????$('#response').html();
????}
????function?connect()?{
????????var?socket?=?new?SockJS('/endpointWisely');?//1連接SockJS的endpoint是“endpointWisely”,與后臺代碼中注冊的endpoint要一樣。
????????stompClient?=?Stomp.over(socket);//2創(chuàng)建STOMP協(xié)議的webSocket客戶端。
????????stompClient.connect({},?function(frame)?{//3連接webSocket的服務(wù)端。
????????????setConnected(true);
????????????console.log('開始進(jìn)行連接Connected:?'?+?frame);
????????????//4通過stompClient.subscribe()訂閱服務(wù)器的目標(biāo)是'/topic/getResponse'發(fā)送過來的地址,與@SendTo中的地址對應(yīng)。
????????????stompClient.subscribe('/topic/getResponse',?function(respnose){
????????????????showResponse(JSON.parse(respnose.body).responseMessage);
????????????});
????????????//4通過stompClient.subscribe()訂閱服務(wù)器的目標(biāo)是'/user/'?+?userId?+?'/msg'接收一對一的推送消息,其中userId由服務(wù)端傳遞過來,用于表示唯一的用戶,通過此值將消息精確推送給一個(gè)用戶
????????????stompClient.subscribe('/user/'?+?userId?+?'/msg',?function(respnose){
????????????????console.log(respnose);
????????????????showResponse1(JSON.parse(respnose.body).responseMessage);
????????????});
????????});
????}
????function?disconnect()?{
????????if?(stompClient?!=?null)?{
????????????stompClient.disconnect();
????????}
????????setConnected(false);
????????console.log("Disconnected");
????}
????function?sendName()?{
????????var?name?=?$('#name').val();
????????//通過stompClient.send()向地址為"/welcome"的服務(wù)器地址發(fā)起請求,與@MessageMapping里的地址對應(yīng)。因?yàn)槲覀兣渲昧藃egistry.setApplicationDestinationPrefixes(Constant.WEBSOCKETPATHPERFIX);所以需要增加前綴/ws-push/
????????stompClient.send("/ws-push/welcome",?{},?JSON.stringify({?'name':?name?}));
????}
????function?showResponse(message)?{
????????var?response?=?$("#response");
????????response.html(message);
????}
????function?showResponse1(message)?{
????????var?response?=?$("#response1");
????????response.html(message);
????}
script>
body>
html>
測試

點(diǎn)擊連接控制臺輸出

表示連接成功并且訂閱了兩個(gè)地址

此時(shí)在文本框內(nèi)輸入任意值,結(jié)果如圖所示則代表成功


控制臺中顯示依次為,發(fā)送信息,目標(biāo)長度內(nèi)容

此時(shí)服務(wù)端控制器接收到請求

同時(shí)給指定用戶發(fā)送了消息,所以控制臺接收到消息

同時(shí)因?yàn)榭刂破饔凶⒔釦SendTo所以會(huì)向@SendTo的地址廣播消息,客戶端訂閱了廣播地址所有控制臺顯示接收了消息

推薦閱讀
評論
圖片
表情
