Spring Boot整合WebSocket


我們首先要知道WebSocket的應用場景:

       ①在線股票網站

       ②即時聊天

       ③多人在線游戲

       ④應用集群通信

       ⑤系統性能及時監控

       ......

下面讓我們開始從項目中學習WebSocket:

(一)首先創建一個Spring Boot項目,如下圖,博主用的是IDEA:

          

          后續過程不太難,如果還是不太會的話,請看https://www.cnblogs.com/Trojan00/p/11317839.html

(二)添加依賴:

          

 

(三)配置WebSocket(WebScoketMessageBroker.java):

 

 1 package com.example.demo;
 2 
 3 import org.springframework.context.annotation.Configuration;
 4 import org.springframework.messaging.simp.config.MessageBrokerRegistry;
 5 import org.springframework.web.socket.config.annotation.EnableWebSocketMessageBroker;
 6 import org.springframework.web.socket.config.annotation.StompEndpointRegistry;
 7 import org.springframework.web.socket.config.annotation.WebSocketMessageBrokerConfigurer;
 8 
 9 @Configuration
10 @EnableWebSocketMessageBroker
11 public class WebScoketMessageBroker implements WebSocketMessageBrokerConfigurer {
12     @Override
13     public void configureMessageBroker(MessageBrokerRegistry config){
14         config.enableSimpleBroker("/topic");
15         config.setApplicationDestinationPrefixes("/app");
16     }
17     @Override
18     public void registerStompEndpoints(StompEndpointRegistry registry){
19         registry.addEndpoint("/chat").withSockJS();
20     }
21 }

 

            講解時間到:

                           ①自定義類WebSocketConfig繼承自WebSocketMessageBrokerConfigurer進

                              行WebSocket配置,然后通過@EnableWebSocketMessageBgroker注解開啟

                              WebSocket消息代理;

                           ②config.enableSimpleBroker("/topic")表示設置消息代理的前綴,即如果消息

                              前綴是"/topic",就會將消息轉發給消息代理(Broker),再由消息代理將消息廣

                              播給當前連接的客戶端。

                          ③config.setApplicationDestinationPrefixes("/app")表示配置一個或多個前綴,

                              通過這些前綴過濾出需要備注接方法處理的消息。例如,前綴為“/app”的d-

                              estination可以通過@MessageMapping注解的方法處理,而其他destination

                              (例如:"/topic" "/queue")將被直接交給broker處理。

                          ④registry.adEndpoing("/chat").withSockJS()則表示定義一個前綴為"/chat"的

                              EndPoint,並開啟sockjs支持,sockjs可以解決瀏覽器對WebSocket的兼容性

                              問題,客戶戶端將通過這里配置的URL來建立WebSocket連接。

(四)定義Controller(GreetingController.java):

 

 1 package org.sang.wschat.controller;
 2 
 3 import org.sang.wschat.model.Chat;
 4 import org.sang.wschat.model.Message;
 5 import org.springframework.beans.factory.annotation.Autowired;
 6 import org.springframework.messaging.handler.annotation.MessageMapping;
 7 import org.springframework.messaging.handler.annotation.SendTo;
 8 import org.springframework.messaging.simp.SimpMessagingTemplate;
 9 import org.springframework.scheduling.annotation.Scheduled;
10 import org.springframework.stereotype.Controller;
11 
12 import java.security.Principal;
13 
14 @Controller
15 public class GreetingController {
16     @Autowired
17     SimpMessagingTemplate messagingTemplate;
18 
19     @MessageMapping("/hello")
20     @SendTo("/topic/greetings")
21     public Message greeting(Message message) throws Exception {
22         return message;
23     }
24 //    @MessageMapping("/chat")
25 //    public void chat(Principal principal, Chat chat) {
26 //        String from = principal.getName();
27 //        chat.setFrom(from);
28 //        messagingTemplate.convertAndSendToUser(chat.getTo(), "/queue/chat", chat);
29 //    }
30 }

 

            講解時間到:

                                 在這一段代碼中我們看到@MessageMapping("/hello")注解的方法將用來接

                                 收"/app/hello"(上一段代碼講解的第三步)路徑發送來的消息,在注解方法

                                 對消息處理后,再將消息轉發到@SendTo定義的路徑上,兒@SendTo路徑是

                                 一個前綴為"/topic"的路徑,所以該消息將被交給消息代理的broker, 之后再

                                由broker進行廣播(上一段代碼講解的第三步)。

         快捷聊天界面構建(chat.html):

 1 <!DOCTYPE html>
 2 <html lang="en">
 3 <head>
 4     <meta charset="UTF-8">
 5     <title>群聊</title>
 6     <script src="/webjars/jquery/jquery.min.js"></script>          //外部JS,添加依賴是有添加
 7     <script src="/webjars/sockjs-client/sockjs.min.js"></script>   //外部JS
 8     <script src="/webjars/stomp-websocket/stomp.min.js"></script>  //外部JS
 9     <script src="/app.js"></script>
10 </head>
11 <body>
12 <div>
13     <label for="name">請輸入用戶名:</label>
14     <input type="text" id="name" placeholder="用戶名">
15 </div>
16 <div>
17     <button id="connect" type="button">連接</button>
18     <button id="disconnect" type="button" disabled="disabled">斷開連接</button>
19 </div>
20 <div id="chat" style="display: none;">
21     <div>
22         <label for="name">請輸入聊天內容:</label>
23         <input type="text" id="content" placeholder="聊天內容">
24     </div>
25     <button id="send" type="button">發送</button>
26     <div id="greetings">
27         <div id="conversation" style="display: none">群聊進行中...</div>
28     </div>
29 </div>
30 </body>
31 </html>

         app.js:

 1 var stompClient = null;
 2 function setConnected(connected) {
 3     $("#connect").prop("disabled", connected);
 4     $("#disconnect").prop("disabled", !connected);
 5     if (connected) {
 6         $("#conversation").show();
 7         $("#chat").show();
 8     }
 9     else {
10         $("#conversation").hide();
11         $("#chat").hide();
12     }
13     $("#greetings").html("");
14 }
15 function connect() {
16     if (!$("#name").val()) {
17         return;
18     }
19     // registry.addEndpoint("/chat").withSockJS()中的那個"/chat"
20     var socket = new SockJS('/chat');  
21     stompClient = Stomp.over(socket);
22     stompClient.connect({}, function (frame) {
23         setConnected(true);
24         // 第一個參數就是目的地地址
25         stompClient.subscribe('/topic/greetings', function (greeting) {
26             showGreeting(JSON.parse(greeting.body));
27         });
28     });
29 }
30 function disconnect() {
31     if (stompClient !== null) {
32         stompClient.disconnect();
33     }
34     setConnected(false);
35 }
36 function sendName() {
37     // 發送,第一個參數就是GreetingController中的發送源地址
38     stompClient.send("/app/hello", {}, JSON.stringify({'name': $("#name").val(),'content':$("#content").val()}));
39 }
40 function showGreeting(message) {
41     $("#greetings").append("<div>" + message.name+":"+message.content + "</div>");
42 }
43 
44 $(function () {
45     //分別是點擊連接、斷開連接、發送三個事件,以及對應出發的函數
46     $( "#connect" ).click(function() { connect(); });
47     $( "#disconnect" ).click(function() { disconnect(); });
48     $( "#send" ).click(function() { sendName(); });
49 });

            運行(打開兩個頁面,進入chat.html,並輸入兩個用戶名,然后點擊連接,就可以輸入內容了

         ,輸入好就可點擊發送了):

         

 

(五) 接下來讓我們看一下點對點的傳送吧!點對點,所以有了用戶,因此用到了spring security,故

           先要添加Spring Security的依賴;

(六)配置Spring SecurityWebSecurityConfig

 

 1 package org.sang.wschat.config;
 2 
 3 import org.springframework.context.annotation.Bean;
 4 import org.springframework.context.annotation.Configuration;
 5 import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
 6 import org.springframework.security.config.annotation.web.builders.HttpSecurity;
 7 import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
 8 import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
 9 import org.springframework.security.crypto.password.PasswordEncoder;
10 
11 @Configuration
12 public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
13     @Bean
14     PasswordEncoder passwordEncoder() {
15         return new BCryptPasswordEncoder();
16     }
17     @Override
18     protected void configure(AuthenticationManagerBuilder auth) throws Exception {
19         auth.inMemoryAuthentication()
20         .withUser("admin")
21         .password("$2a$10$RMuFXGQ5AtH4wOvkUqyvuecpqUSeoxZYqilXzbz50dceRsga.WYiq") //即123 22         .roles("admin")
23         .and()
24         .withUser("sang")
25         .password("$2a$10$RMuFXGQ5AtH4wOvkUqyvuecpqUSeoxZYqilXzbz50dceRsga.WYiq") //即123 26         .roles("user");
27     }
28     @Override
29     protected void configure(HttpSecurity http) throws Exception {
30         http.authorizeRequests()
31                 .anyRequest().authenticated()
32                 .and()
33                 .formLogin().permitAll();
34     }
35 }

 

(七)改造WebSocket(WebSocketConfig.java):

 1 package org.sang.wschat.config;
 2 
 3 import org.springframework.context.annotation.Configuration;
 4 import org.springframework.messaging.simp.config.MessageBrokerRegistry;
 5 import org.springframework.web.socket.config.annotation.EnableWebSocketMessageBroker;
 6 import org.springframework.web.socket.config.annotation.StompEndpointRegistry;
 7 import org.springframework.web.socket.config.annotation.WebSocketMessageBrokerConfigurer;
 8 
 9 @Configuration
10 @EnableWebSocketMessageBroker
11 public class WebSocketConfig implements WebSocketMessageBrokerConfigurer {
12     @Override
13     public void configureMessageBroker(MessageBrokerRegistry config) {
14         config.enableSimpleBroker("/topic","/queue");  //就這里多加了個"/queue" 15         config.setApplicationDestinationPrefixes("/app");
16     }
17     @Override
18     public void registerStompEndpoints(StompEndpointRegistry registry) {
19         registry.addEndpoint("/chat").withSockJS();
20     }
21 }

(八)配置Controller:

 1 package org.sang.wschat.controller;
 2 
 3 import org.sang.wschat.model.Chat;
 4 import org.sang.wschat.model.Message;
 5 import org.springframework.beans.factory.annotation.Autowired;
 6 import org.springframework.messaging.handler.annotation.MessageMapping;
 7 import org.springframework.messaging.handler.annotation.SendTo;
 8 import org.springframework.messaging.simp.SimpMessagingTemplate;
 9 import org.springframework.scheduling.annotation.Scheduled;
10 import org.springframework.stereotype.Controller;
11 
12 import java.security.Principal;
13 
14 @Controller
15 public class GreetingController {
16     @Autowired
17     SimpMessagingTemplate messagingTemplate;
18 
19     @MessageMapping("/hello")
20     @SendTo("/topic/greetings")
21     public Message greeting(Message message) throws Exception {
22         return message;
23     }
24     @MessageMapping("/chat")
25     public void chat(Principal principal, Chat chat) {
26         String from = principal.getName();
27         chat.setFrom(from);
28         messagingTemplate.convertAndSendToUser(chat.getTo(), "/queue/chat", chat);  
29     }
30 }

(九)創建聊天界面(類似於chat.html,此處是onlinechat.html):

 1 <!DOCTYPE html>
 2 <html lang="en">
 3 <head>
 4     <meta charset="UTF-8">
 5     <title>單聊</title>
 6     <script src="/webjars/jquery/jquery.min.js"></script>
 7     <script src="/webjars/sockjs-client/sockjs.min.js"></script>
 8     <script src="/webjars/stomp-websocket/stomp.min.js"></script>
 9     <script src="/chat.js"></script>
10 </head>
11 <body>
12 <div id="chat">
13     <div id="chatsContent">
14     </div>
15     <div>
16         請輸入聊天內容:
17         <input type="text" id="content" placeholder="聊天內容">
18         目標用戶:
19         <input type="text" id="to" placeholder="目標用戶">
20         <button id="send" type="button">發送</button>
21     </div>
22 </div>
23 </body>
24 </html>

(十)該輪到chat.js了,類似於點對點之前的那個項目的app.js:

 1 var stompClient = null;
 2 function connect() {
 3     var socket = new SockJS('/chat');
 4     stompClient = Stomp.over(socket);
 5     stompClient.connect({}, function (frame) {
 6         stompClient.subscribe('/user/queue/chat', function (chat) {
 7             showGreeting(JSON.parse(chat.body));
 8         });
 9     });
10 }
11 function sendMsg() {
12     stompClient.send("/app/chat", {}, JSON.stringify({'content':$("#content").val(), 'to':$("#to").val()}));
13 }
14 function showGreeting(message) {
15     $("#chatsContent").append("<div>" + message.from+":"+message.content + "</div>");
16 }
17 $(function () {
18     connect();
19     $( "#send" ).click(function() { sendMsg(); });
20 });

            注:點對點這里要注意,第6行,路徑是"/user/queue/chat",因為這個destinationPrefix默認值是

          "/user",也就是說消息的最終發送路徑是"/user/用戶名/queue.chat"

          結果如下:

          

 

ADD:其實這個里面還有兩個Mode,一個是Message的,另一個是Chat的,讀者可以下載完整源代碼自

           行研究,這個項目的完整源代碼地址:https://github.com/Stray-Kite/WsChat


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM