直奔主題:
一般的web項目都是短連接,主動權是交給客戶端手里,在客戶端不發請求的情況下,服務端是沒辦法主動給客戶端發送消息。但是有些情況下,我們需要長連接,比如常見到的聊天室。網上有很多的案例這里就不多說了!()
java后端部分
首先,我們是需要導入maven節點
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-websocket</artifactId> </dependency>
傳統的玩法,需要一個配置類WebSocketConfiguration,他需要實現WebSocketMessageBrokerConfigurer。重寫兩個方法configureMessageBroker,registerStompEndpoints
package com.lhf.novel.config.socket; 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; /** * WebSocketConfigufation實體類 * * @author zy * @since 2020/3/20$ */ @Configuration @EnableWebSocketMessageBroker //@Deprecated public class WebSocketConfiguration implements WebSocketMessageBrokerConfigurer { @Override public void configureMessageBroker(MessageBrokerRegistry registry) { registry.enableSimpleBroker("/topic");//注冊一個隊列,主要用來做消息區分的(在我看來) } @Override public void registerStompEndpoints(StompEndpointRegistry registry) { registry.addEndpoint("/api").setAllowedOrigins("*").addInterceptors().withSockJS();//通俗易懂簡單的來講,addEndpoint("/api")就是客戶端連接的時候url地址,后邊的就不解釋了。。。
} }
這樣客戶端就可以連接了。
現在添加一個測試,主動給客戶端推送消息********
package com.lhf.novel.config.socket; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.messaging.simp.SimpMessagingTemplate; import org.springframework.scheduling.annotation.Scheduled; import org.springframework.stereotype.Component; /** * MessageService實體類 * * @author zy * @since 2020/3/22 */ @Component public class MessageService { @Autowired private SimpMessagingTemplate messagingTemplate; @Scheduled(fixedDelay = 5000)//spring的計划任務,這里表示每五秒執行一次,需要在配置類或者啟動類上加一個注解@EnableScheduling,這樣才能生效
public void mes() { messagingTemplate.convertAndSend("/topic","125"); } }
這里SimpMessagingTemplate是內置的一個消息模板,這里使用他的convertAndSend()方法,第一個參數是表示向那個通道發送、第二個參數是發送的內容這樣客戶端就可以連接了。
vue前端部分
首先需要導入兩個依賴
npm install socket-client
npm install stompjs
import SockJS from 'sockjs-client'; import Stomp from 'stompjs'; export default { name: "Index", data() { return { socket: null, stompClient: null, name: 'admin', mes: '', cmes: '', } }, mounted() { this.init(); }, destroyed() { this.stompClient.destroyed(); }, methods: { init() { let _that = this;
//連接socket this.socket = new SockJS('http://localhost:8080/api'); let stompClient = Stomp.over(this.socket); stompClient.connect({ login: 'admin', passcode: '123456' }, function (frame) { window.console.log(frame)
//訂閱,這里訂閱服務端注冊過的topic stompClient.subscribe('/topic', function (message) { window.console.warn(message); _that.cmes = message.body // window.console.log(JSON.parse(message.body)); }); }) this.stompClient = stompClient; }, send() {
//這是發送消息 this.stompClient.send("/mes", {}, "客戶端發來消息") } }, }
這樣前端也差不多完成了
打開開發者模式 F12可以看到,每五秒服務端都會推送一條消息過來。
以上是服務端主動發送消息,聊天窗一定是點多點,或者點對多的模式的,那么就需要有用戶的概念,這是就需要加上springsecurity了。
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-security</artifactId> </dependency>
security配置部分省略。。。我的配置用有兩個用戶admin 和 root
這樣需要一個controller
package com.lhf.novel.config.socket; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.messaging.handler.annotation.DestinationVariable; import org.springframework.messaging.handler.annotation.MessageMapping; import org.springframework.messaging.handler.annotation.SendTo; import org.springframework.messaging.simp.SimpMessagingTemplate; import org.springframework.web.bind.annotation.RestController; import java.security.Principal; /** * TestController實體類 * * @author zy * @since 2020/3/22 */ @RestController public class TestController { @Autowired private SimpMessagingTemplate messagingTemplate; @MessageMapping("/mes") //@SendTo("/topic") public void mes(Principal principal, String mess) { System.out.println("name:"+principal.getName()); System.out.println("mes = " + mess); messagingTemplate.convertAndSendToUser("admin","/topic","我來了"); // return principal.getName()+":發來致電"; } }
@MessageMapping和RequestMapping作用是一樣的,設置請求路徑
@SendTo是在有返回值的情況下返回通道
Principal是SpringSecurity的對象,里邊包含用戶的所有信息,當然是你自己的。
mess 是接受的消息,這里為了簡單我是直接寫了一個String類型的,你也可以分裝一個對象,這樣會好一些,畢竟點對點你得告訴服務器你要發送給誰,發送的內容。
SimpMessagingTemplate: 消息模板 上邊用了 convertAndSend()的方法,現在我們需要用到convertAndSendToUser()看方法名字也能看出來,這個是發送給指定用戶的,第一個參數是要發送的指定用戶,第二個參數通道、第三個是發送的內容(不要被我迷惑了,第 三個參數是個Object的)
<template> <div> {{ $store.state.realName }}: <el-input v-model="mes"/> {{ cmes }} <el-button @click="send">點擊</el-button> </div> </template> <script> import SockJS from 'sockjs-client'; import Stomp from 'stompjs'; export default { name: "Index", data() { return { socket: null, stompClient: null, name: 'admin', mes: '', cmes: '', } }, mounted() { this.init(); }, destroyed() { // this.stompClient.destroyed(); }, methods: { init() { let _that = this; this.socket = new SockJS('http://localhost:8080/api'); let stompClient = Stomp.over(this.socket); stompClient.connect({ login: 'admin', passcode: '123456' }, function (frame) { window.console.log(frame) stompClient.subscribe('/user/topic', function (message) { window.console.warn(message); _that.cmes = message.body // window.console.log(JSON.parse(message.body)); }); }) this.stompClient = stompClient; }, send() { this.stompClient.send("/mes", {}, "客戶端發來消息") } }, } </script> <style scoped> </style>
上訴代碼有一點不同就是客戶端定訂閱的多了一個spring提供的/user,也就是說現在需要訂閱 /user/topic兩個了
然后看效果圖
至此結束,文筆不好,歡迎吐槽 1490030544,備注+ 葬月