SpringBoot實(shí)現(xiàn)WebSocket發(fā)送接收消息 + Vue實(shí)現(xiàn)SocketJs接收發(fā)送消息
參考:
1、https://www.mchweb.net/index.php/dev/887.html
2、https://itonline.blog.csdn.net/article/details/81221103?spm=1001.2101.3001.6661.1&utm_medium=distribute.pc_relevant_t0.none-task-blog-2~default~CTRLIST~default-1-81221103-blog-121078449.pc_relevant_aa&depth_1-utm_source=distribute.pc_relevant_t0.none-task-blog-2~default~CTRLIST~default-1-81221103-blog-121078449.pc_relevant_aa&utm_relevant_index=1
3、https://blog.csdn.net/yingxiake/article/details/51224569
使用場(chǎng)景
廣播模式 :使用場(chǎng)景:給所有連接了這個(gè)通道的客戶端發(fā)送消息。
- convertAndSend()
- @SendTo
點(diǎn)對(duì)點(diǎn)模式 :使用場(chǎng)景:?jiǎn)为?dú)給當(dāng)前用戶發(fā)送消息。
-
下面兩種方式,都默認(rèn)加了一個(gè)前綴:/user
-
convertAndSendToUser()
-
@SendToUser
一、后端SpringBoot + WebSocket基礎(chǔ)配置
1、導(dǎo)包
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-websocket</artifactId>
</dependency>
2、配置config
- 細(xì)節(jié):必須配置跨域。低版本的SpringBoot(2.1.5.RELEASE 就不行)不行,需要使用高版本。低版本的解決方案還未找到。
- 跨域配置使用:.setAllowedOriginPatterns("*")。。不能使用:.setAllowedOrigins("*")
package com.cc.ws.config;
import org.springframework.context.annotation.Configuration;
import org.springframework.messaging.simp.config.MessageBrokerRegistry;
import org.springframework.web.socket.config.annotation.EnableWebSocketMessageBroker;
import org.springframework.web.socket.config.annotation.StompEndpointRegistry;
import org.springframework.web.socket.config.annotation.WebSocketMessageBrokerConfigurer;
/**
* <p>@EnableWebSocketMessageBroker 的作用</p>
* <li>注解開(kāi)啟使用STOMP協(xié)議來(lái)傳輸基于代理(message broker)的消息,</li>
* <li>這時(shí)控制器支持使用 @MessageMapping,就像使用 @RequestMapping一樣</li>
* @author cc
*
*/
@Configuration
@EnableWebSocketMessageBroker
public class WebSocketConfig implements WebSocketMessageBrokerConfigurer {
/** <p>啟動(dòng)簡(jiǎn)單Broker</p>
* <p>表示客戶端訂閱地址的前綴信息,也就是客戶端接收服務(wù)端消息的地址的前綴信息</p>
* <p>代理的名字:都是自定義的</p>
*
* /user 點(diǎn)對(duì)點(diǎn)(默認(rèn)也是/user,可以自定義,但是必須和setUserDestinationPrefix中的設(shè)置一致)
* /topic1 廣播模式1
* /topic2 廣播模式2
*
* /mass 廣播模式:群發(fā)
*/
@Override
public void configureMessageBroker(MessageBrokerRegistry registry) {
registry.enableSimpleBroker(
"/user", "/topic1", "/topic2", "/mass"
);
// 點(diǎn)對(duì)點(diǎn)使用的訂閱前綴(客戶端訂閱路徑上會(huì)體現(xiàn)出來(lái)),不設(shè)置的話,默認(rèn)也是 /user/
// 注意,這里必須和上面設(shè)置的Broker:/user 一致(兩個(gè)都可以自定義,但必須一致)。否則連接不上
registry.setUserDestinationPrefix("/user/");
// 指服務(wù)端接收地址的前綴,意思就是說(shuō)客戶端給服務(wù)端發(fā)消息的地址的前綴
// registry.setApplicationDestinationPrefixes("/socket");
}
/**
* 這個(gè)方法的作用是添加一個(gè)服務(wù)端點(diǎn),來(lái)接收客戶端的連接。
* registry.addEndpoint("/socket")表示添加了一個(gè)/socket端點(diǎn),客戶端(前端)就可以通過(guò)這個(gè)端點(diǎn)來(lái)進(jìn)行連接。
* withSockJS()的作用是開(kāi)啟SockJS支持。
* @param registry registry
*/
@Override
public void registerStompEndpoints(StompEndpointRegistry registry) {
// 注冊(cè)一個(gè)STOMP的endpoint端點(diǎn),并指定使用SockJS協(xié)議
// 前端使用這個(gè)地址連接后端 WebSocket接口
registry.addEndpoint("/broadcast", "/point")
// 允許所有源跨域。還可以指定ip配置:http://ip:*
// 低版本的SpringBoot(2.1.5.RELEASE 就不行)不行
.setAllowedOriginPatterns("*")
.withSockJS();
}
}
3、啟動(dòng)類,配置定時(shí)器
- @EnableScheduling
@SpringBootApplication
@EnableScheduling
public class WebSocketDemoApplication {
public static void main(String[] args) {
SpringApplication.run(WebSocketDemoApplication.class, args);
}
}
二、前端基礎(chǔ)配置
let socket1 = new SockJS('http://服務(wù)器ip:服務(wù)器端口/broadcast');
let stompClient1 = Stomp.over(socket1);//廣播模式
stompClient1.connect({}, (frame) => {
stompClient1.subscribe('/topic1/', (message) => {
console.log(message.body);
});
});
三、后端不接收,只發(fā)送
-
使用spring Scheduled 定時(shí)發(fā)送消息
-
直接使用:SimpMessagingTemplate 的 convertAndSend廣播模式 和 convertAndSendToUser點(diǎn)對(duì)點(diǎn)模式
1、后端
- 注意點(diǎn)對(duì)點(diǎn)發(fā)送:
convertAndSendToUser的默認(rèn)前綴(/user)是在WebSocketConfig配置文件中配置的。文章來(lái)源:http://www.zghlxwxcb.cn/news/detail-450540.html
代碼:文章來(lái)源地址http://www.zghlxwxcb.cn/news/detail-450540.html
@Resource
private SimpMessagingTemplate simpMsgTemp;
/** 廣播推送消息1:會(huì)發(fā)送給所有連接了 topic1 這個(gè)通道的客戶端。
* topic1:在Broker中配置
**/
@Scheduled(cron = "0/1 * * * * ?")
public void getSocket(){
String msg = String.format("%s 的第 %s 個(gè)消息", "topic1", LocalDateTime.now().getSecond());
log.info("{}",msg);
simpMsgTemp.convertAndSend("/topic1/", msg);
}
/** 廣播推送消息2:多指定一個(gè)uri。相當(dāng)于另一條通道(推薦使用)
* <li>自定義url后綴,還可以實(shí)現(xiàn)用戶和用戶單點(diǎn)發(fā)送。</li>
* topic2:在Broker中配置
* custom:是自定義的
*/
@Scheduled(cron = "0/1 * * * * ?")
public void getSocketUser(){
String msg = String.format("topic2 的第 %s 個(gè)消息", LocalDateTime.now().getSecond());
log.info("{}",msg);
simpMsgTemp.convertAndSend("/topic2/custom" ,msg);
}
/**點(diǎn)對(duì)點(diǎn)發(fā)送 convertAndSendToUser(第一個(gè)參數(shù):一般是用戶id)
* -> 假如用戶id是1。用用戶id是1的在兩個(gè)地方登陸了客戶端(比如不同的瀏覽器登陸同一個(gè)用戶),
* -> convertAndSendToUser會(huì)把消息發(fā)送到用戶1登陸的兩個(gè)客戶端中
* 發(fā)送到:/user/{userId}/cs 下。cs是自定義的,且必須自定義一個(gè)。
*/
@Scheduled(cron = "0/1 * * * * ?")
public void pointToPoint(){
//這個(gè)用戶id是后端獲取的當(dāng)前登陸的用戶id
String userId = "123";
String msg = String.format("點(diǎn)對(duì)點(diǎn):第 %s 個(gè)消息。用戶id:%s", LocalDateTime.now().getSecond(), userId);
log.info("{}",msg);
//發(fā)送
simpMsgTemp.convertAndSendToUser(userId,"/cs/" ,msg);
}
2、前端
- 注意點(diǎn)對(duì)點(diǎn)的接收方式,用戶id需要取出前端存的用戶id
//這樣才能同時(shí)接收后端來(lái)的三套不同通道的消息。
// broadcast 和后端:registerStompEndpoints中的配置必須一致
// point 和后端:registerStompEndpoints中的配置必須一致
// broadcast、point 也可以只用一個(gè),這里只是為了好區(qū)分。
let socket1 = new SockJS('http://yourIp:端口/broadcast');
let socket2 = new SockJS('http://yourIp:端口/broadcast');
let socket3 = new SockJS('http://yourIp:端口/point');
// console.log("wb:" + socket)
let stompClient1 = Stomp.over(socket1);
let stompClient2 = Stomp.over(socket2);
let stompClient3 = Stomp.over(socket3);
// ----------------廣播模式1--------------------
stompClient1.connect({}, (frame) => {
console.log('-----------frame1', frame)
stompClient1.subscribe('/topic1/', (message) => {
console.log(message.body);
this.msg = message.body;
// console.log(JSON.parse(message.body));
});
});
// ----------------廣播模式2--------------------
stompClient2.connect({}, (frame) => {
console.log('-----------frame2', frame)
stompClient2.subscribe('/topic2/custom', (message) => {
console.log(message.body);
this.user = message.body;
// console.log(JSON.parse(message.body));
});
});
// ----------------點(diǎn)對(duì)點(diǎn)模式--------------------
//前端獲取的 userId
let userId = '123';
//連接WebSocket服務(wù)端
stompClient3.connect({},(frame) => {
console.log('Connected:' + frame);
stompClient3.subscribe('/user/' + userId + '/cs/',
(response) => {
this.peer = response.body;
});
});
四、后端接收、接收后再發(fā)送
- 也可以只接收消息,不發(fā)送。看業(yè)務(wù)需求。
- 使用 @MessageMapping 接收前端發(fā)送過(guò)來(lái)的消息
- 使用:@SendTo 廣播模式、@SendToUser 點(diǎn)對(duì)點(diǎn)模式
- 使用:SimpMessagingTemplate 的 convertAndSend廣播模式 和 convertAndSendToUser 點(diǎn)對(duì)點(diǎn)模式
1、后端
@Resource
private SimpMessagingTemplate simpMsgTemp;
/** <p>廣播模式一、接收前端的消息,處理后給前端返回一個(gè)消息。</p>
* <li>后端 把消息處理后 發(fā)送到 /mass/getResponse 路徑下</li>
* <ol>
* <li>@MessageMapping("/massRequest1") :作用:接收前端來(lái)的消息。類似于@RestController</li>
* <li>@SendTo("/mass/getResponse1"):作用跟convertAndSend類似,廣播發(fā)給與該通道相連的客戶端。SendTo 發(fā)送至 Broker 下的指定訂閱路徑 </li>
* <li>@SendToUser("/mass/getResponse1"):作用跟convertAndSendToUser類似,定點(diǎn)發(fā)送。SendTo 發(fā)送至 Broker 下的指定訂閱路徑 </li>
* <li>/mass 必須在配置文件配置</li>
* <li>/getResponse1 自定義的后綴</li>
* </ol>
*/
@MessageMapping("/massRequest1")
@SendTo("/mass/getResponse1")
public String mass1(String chatRoomRequest){
//處理前端消息……
log.info("前端消息:{}",chatRoomRequest);
//返回消息
return "@SendTo 廣播一(單次) 后端處理完成!";
}
/** 廣播模式二、接收前端的消息,可以多次給前端發(fā)消息
* <li>/mass 必須在配置文件配置</li>
* <li>/getResponse2 自定義的后綴</li>
*/
@MessageMapping("/massRequest2")
public void mass2(String chatRoomRequest){
log.info("前端的消息:{}",chatRoomRequest);
for (int i = 0; i < 5; i++) {
String msg = "后端處理后的 廣播二(多次):" + i;
simpMsgTemp.convertAndSend("/mass/getResponse2", msg);
}
simpMsgTemp.convertAndSend("/mass/getResponse2",
"后端處理后的 廣播二(多次),后端處理完成!");
}
/** <p>點(diǎn)對(duì)點(diǎn)一、接收前端消息,只能返回一次消息(必須登陸系統(tǒng)才能使用。)</p>
* <li>只有發(fā)送原始消息的客戶端才會(huì)收到響應(yīng)消息,而不是所有連接的客戶端都會(huì)收到響應(yīng)消息。</li>
* <li>/alone/getResponse1:自定義的,不需要在配置文件配置。</li>
*
* <p>@SendToUser</p>
* <li>默認(rèn)該注解前綴為 /user</li>
* <li>broadcast屬性,表明是否廣播。就是當(dāng)有同一個(gè)用戶登錄多個(gè)session時(shí),是否都能收到。取值true/false.</li>
*
* @param principal Principal :登陸用戶的信息,需要使用spring s安全框架寫(xiě)入信息?
*/
@MessageMapping("/aloneRequest1")
@SendToUser("/alone/getResponse1")
public String alone1(String chatRoomRequest){
//處理前端消息……
log.info("前端消息:{}",chatRoomRequest);
//返回消息
return "@SendToUser 點(diǎn)對(duì)點(diǎn)一(單次) 后端處理完成!";
}
/** 點(diǎn)對(duì)點(diǎn)二、接收前端消息,可以多次給前端發(fā)消息
* <li>convertAndSendToUser —— 發(fā)送消息給指定用戶id的</li>
* <li>如果用戶1在兩個(gè)地方(A/B)登陸可以客戶端,并且連接了該通道,其中一個(gè)如A給后端發(fā)消息,后端返回消息,A/B兩個(gè)地方都會(huì)收到消息</li>
* <ol>
* <li>@MessageMapping("/aloneRequest2") 接收前端指定用戶消息,</li>
* <li>/alone/getResponse2 不用在配置文件中配置</li>
* <li>返回消息 發(fā)送到 user/{userId}/alone/getResponse2 下 (定點(diǎn)發(fā)送)</li>
* </ol>
*/
@MessageMapping("/aloneRequest2")
public void alone2(String chatRoomRequest){
//后端獲取的當(dāng)前登陸的用戶的id(和前端一致)
String userId = "456";
log.info("前端的消息:{}",chatRoomRequest);
for (int i = 0; i < 5; i++) {
String msg = "后端處理后的 點(diǎn)對(duì)點(diǎn)二(多次):" + i;
simpMsgTemp.convertAndSendToUser(userId,"/alone/getResponse2", msg);
}
simpMsgTemp.convertAndSendToUser(userId,"/alone/getResponse2",
"后端處理后的 點(diǎn)對(duì)點(diǎn)二(多次),后端處理完成!");
}
2、前端
- 3點(diǎn)對(duì)點(diǎn)一 未實(shí)現(xiàn)。
//連接SockJS的 broadcast
let socket1 = new SockJS('http://yourIp:端口/broadcast');
let socket2 = new SockJS('http://yourIp:端口/broadcast');
let socket3 = new SockJS('http://yourIp:端口/point');
let socket4 = new SockJS('http://yourIp:端口/point');
//使用STMOP子協(xié)議的WebSocket客戶端
let stompClient1 = Stomp.over(socket1);
let stompClient2 = Stomp.over(socket2);
let stompClient3 = Stomp.over(socket3);
let stompClient4 = Stomp.over(socket4);
//1廣播模式一
stompClient1.connect({},(frame) => {
console.log('廣播模式一:' + frame);
//1發(fā)送消息
stompClient1.send("/massRequest1",{},"我是前端來(lái) 廣播模式一 的消息!");
//2接收消息
stompClient1.subscribe('/mass/getResponse1',(response) => {
this.broadcast1 = response.body
});
});
//2廣播模式二
stompClient2.connect({},(frame) => {
console.log('廣播模式二:' + frame);
//1發(fā)送消息
stompClient2.send("/massRequest2",{},"我是前端來(lái) 廣播模式二 的消息");
//2接收消息
stompClient2.subscribe('/mass/getResponse2',(response) => {
this.broadcast2 = response.body
});
});
//3點(diǎn)對(duì)點(diǎn)一 :必須登陸系統(tǒng)才能實(shí)現(xiàn)。要往:Principal設(shè)置用戶登陸信息才行
//1發(fā)送消息
// stompClient3.send("/aloneRequest1",{},"我是前端來(lái) 點(diǎn)對(duì)點(diǎn)一 的消息");
stompClient3.connect({},(frame) => {
console.log('點(diǎn)對(duì)點(diǎn)一1:' + frame);
stompClient3.send("/aloneRequest1",{},"我是前端來(lái) 點(diǎn)對(duì)點(diǎn)一 的消息");
//2接收消息
stompClient3.subscribe('/user/alone/getResponse1' ,(response) => {
console.log('-------response.body', response.body)
this.point1 = response.body
});
});
//4點(diǎn)對(duì)點(diǎn)二:必須獲取現(xiàn)在登陸了的用戶id,且必須和后端一致才行。
stompClient4.connect({},(frame) => {
console.log('點(diǎn)對(duì)點(diǎn)二:' + frame);
//1發(fā)送消息
stompClient4.send("/aloneRequest2",{},"我是前端來(lái) 點(diǎn)對(duì)點(diǎn)二 的消息");
//2接收消息
//前端獲取的當(dāng)前登陸的用戶userId(和后端一致)
let userId = '456';
stompClient4.subscribe('/user/'+userId+'/alone/getResponse2',(response) => {
this.point2 = response.body
});
});
到了這里,關(guān)于SpringBoot實(shí)現(xiàn)WebSocket發(fā)送接收消息 + Vue實(shí)現(xiàn)SocketJs接收發(fā)送消息的文章就介紹完了。如果您還想了解更多內(nèi)容,請(qǐng)?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!