WebSocket 是一種在單個 TCP 鏈接上進行全雙工通訊的協議javascript
根據這個定義有兩個注意的地方:html
協議就是相互通訊的計算機雙方必須共同遵照的一組約定。vue
1)HTTP協議基於 TCP 協議,創建連接必須經過三次握手才能發送信息。java
2)http連接分爲短連接,長連接,短連接是每次請求都要三次握手才能發送本身的信息。即每個request 對應一個 response。長連接是在必定的期限內保持連接。保持TCP鏈接不斷開。客戶端與服務器通訊,必需要有客戶端發起而後服務器返回結果。客戶端是主動的,服務器是被動的。 jquery
3)WebSocket 他是爲了解決客戶端發起多個 http 請求到服務器資源瀏覽器必需要通過長時間的輪訓問題而生的,他實現了多路複用,他是全雙工通訊。在 webSocket 協議下客服端和瀏覽器能夠同時發送信息。web
@Configuration @EnableWebSocketMessageBroker //經過此註解開啓 WebSocket 消息代理 public class WebSocketConfig implements WebSocketMessageBrokerConfigurer { @Override public void configureMessageBroker(MessageBrokerRegistry config) { //設置消息代理的前綴 // 若是消息的前綴是 /topic 就會將消息轉發給消息代理(broker)再由消息代理轉發給全部鏈接的客戶端 config.enableSimpleBroker("/topic"); //客戶端接收服務端消息的地址前綴 //配置一個或多個前綴,經過這些前綴過濾出須要被註解方法處理的消息。 // 例如前綴爲"/app"的 destination 能夠經過 @MessageMapping 註解的方法處理,而其餘 destination("/topic","/query") 將被直接交給 broker 處理 config.setApplicationDestinationPrefixes("/app"); //客戶端給服務端發消息的地址前綴 } @Override public void registerStompEndpoints(StompEndpointRegistry registry) { //定義一個前綴爲 "chat" 的endpoint,並開啓 sockJs支持。 // sockJs 能夠解決對 WebSocket 的兼容性問題,客戶端將經過這裏配置的 url 創建 WebSocket 鏈接 registry.addEndpoint("/chat").withSockJS(); } }
@Controller public class GreetingController { /** * 執行步驟: * 1,由 WebSocketConfig 中的配置,@MessageMapping 註解接收 "/app/hello" 路徑發來的消息 * 2,註解方法對消息進行處理後,將消息轉發到 @SendTo 定義的路徑上 * 3,@SendTo 定義的路徑是一個前綴爲 "/topic" 的路徑,由配置文件,此消息將被交給消息代理 broker,由 broker 進行廣播 * @param message * @return */ @MessageMapping("/hello") @SendTo("/topic/greetings") public Message greeting(Message message) { return message; } }
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>羣聊</title> <script src="/webjars/jquery/jquery.min.js"></script> <script src="/webjars/sockjs-client/sockjs.min.js"></script> <script src="/webjars/stomp-websocket/stomp.min.js"></script> <script src="/app.js"></script> </head> <body> <div> <label for="name">請輸入用戶名</label> <input type="text" id="name" placeholder="用戶名"> </div> <div> <button id="connect"type="button">鏈接</button> <button id="disconnect"type="button" disabled="disabled">斷開鏈接</button> </div> <div id="chat" style="display: none;"> <div> <label for="name">請輸入聊天內容</label> <input type="text" id="content" placeholder="聊天內容"> </div> <button id="send" type="button">發送</button> <div id="greetings"> <div id="conversation" style="display: none">羣聊進行中</div> </div> </div> </body> </html>
var stompClient = null; //頁面顯示設置 function setConnected(connected) { $("#connect").prop("disabled", connected); $("#disconnect").prop("disabled", !connected); if (connected) { $("#conversation").show(); $("#chat").show(); } else { $("#conversation").hide(); $("#chat").hide(); } $("#greeting").html(""); } //創建一個 WebSocket 鏈接,創建鏈接以前必須輸入用戶名 function connect() { if (!$("#name").val()) { return; } //建立一個 SockeJS 實例 var socket = new SockJS('/chat'); //使用stomp.over方式建立一個stompClient,完成客戶端的建立。 stompClient = Stomp.over(socket); stompClient.connect({}, function (frame) { //進行頁面設置 setConnected(true); //使用 subscribe 方法訂閱服務端發送回來的消息,並將服務端發送的消息展現出來 stompClient.subscribe('/topic/greetings', function (greetings) { showGreeting(JSON.parse(greetings.body)); }) }) } //斷開 WebSocket 鏈接 function disconnect() { if (stompClient != null) { stompClient.disconnect(); } setConnected(false) } //發送信息 function sendName() { stompClient.send("/app/hello", {}, JSON.stringify({'name': $('#name').val(), 'content': $('#content').val()})); } //展現信息 function showGreeting(message) { $('#greetings') .append("<div>" + message.name + ":" + message.content + "</div>") } $(function () { //創建鏈接 $("#connect").click(function () { connect(); }); //斷開鏈接 $("#disconnect").click(function () { disconnect(); }); //發送信息 $("#send").click(function () { sendName(); }); })
1)maven 引入依賴錯誤(儘可能去 maven 的中央倉庫拷貝依賴)spring
2)stomp 協議的引入 瀏覽器
使用STOMP的好處在於,它徹底就是一種消息隊列模式,你可使用生產者與消費者的思想來認識它,發送消息的是生產者,接收消息的是消費者。而消費者能夠經過訂閱不一樣的destination,來得到不一樣的推送消息,不須要開發人員去管理這些訂閱與推送目的地以前的關係。
案例見spring官網就有一個簡單的spring-boot的stomp-demo,若是是基於springboot,你們能夠根據spring上面的教程試着去寫一個簡單的demo。springboot
/** * 1. @MessageMapping("/hello") Spring提供一個 @MessageMapping 註解實現了對 WebScoket 的封裝 * 2. SimpMessagingTemplate 是 Spring-WebSocket 內置的一個消息發送的工具 * @param message * @throws Exception */ @Autowired private SimpMessagingTemplate simpMessagingTemplate; @MessageMapping("/hello") public void greeting(Message message) throws Exception{ //使用這個方法進行消息的轉發發送 simpMessagingTemplate.convertAndSend("/topic/greetings",message); }
剛剛實現的功能是羣發消息,下面看下私聊是如何實現的。點對點通訊須要配置多個用戶,咱們用 SpringSecurity 添加兩個用戶。服務器
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-security</artifactId> </dependency>
@Configuration public class WebSecurityConfig extends WebSecurityConfigurerAdapter { @Bean PasswordEncoder passwordEncoder() { return new BCryptPasswordEncoder(); } @Override protected void configure(AuthenticationManagerBuilder auth) throws Exception { auth.inMemoryAuthentication() //添加兩個用戶 admin,sang,密碼設爲123。 .withUser("admin") .password("$2a$10$RMuFXGQ5AtH4wOvkUqyvuecpqUSeoxZYqilXzbz50dceRsga.WYiq") .roles("admin") .and() .withUser("sang") .password("$2a$10$RMuFXGQ5AtH4wOvkUqyvuecpqUSeoxZYqilXzbz50dceRsga.WYiq") .roles("user"); } @Override protected void configure(HttpSecurity http) throws Exception { http.authorizeRequests() .anyRequest().authenticated() .and() .formLogin().permitAll(); } }
@Configuration @EnableWebSocketMessageBroker //經過此註解開啓 WebSocket 消息代理 public class WebSocketConfig implements WebSocketMessageBrokerConfigurer { @Override public void configureMessageBroker(MessageBrokerRegistry config) { //客戶端接收服務端消息的地址前綴 //在羣發的基礎上,添加一個客戶端接收地址的前綴。 config.enableSimpleBroker("/topic","/queue"); //客戶端給服務端發消息的地址前綴 config.setApplicationDestinationPrefixes("/app"); } @Override public void registerStompEndpoints(StompEndpointRegistry registry) { //定義一個前綴爲 "chat" 的endpoint,並開啓 sockJs支持。 // sockJs 能夠解決對 WebSocket 的兼容性問題,客戶端將經過這裏配置的 url 創建 WebSocket 鏈接 registry.addEndpoint("/chat").withSockJS(); } }
@Controller public class GreetingController { @Autowired private SimpMessagingTemplate simpMessagingTemplate; //羣發消息使用 @SendTo 註解 @MessageMapping("/hello") @SendTo("/topic/greetings") public Message greeting(Message message) throws Exception{ return message; } //點對點發送消息使用 SimpMessagingTemplate 實現 @MessageMapping("/chat") //來自 "/app/chat" 的消息將會被此方法處理 public void chat(Principal principal, Chat chat)throws Exception{ String from = principal.getName(); chat.setFrom(from); simpMessagingTemplate.convertAndSendToUser(chat.getTo(),"/queue/chat",chat); } }
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>羣聊</title> <script src="/webjars/jquery/jquery.min.js"></script> <script src="/webjars/sockjs-client/sockjs.min.js"></script> <script src="/webjars/stomp-websocket/stomp.min.js"></script> <script src="/chat.js"></script> </head> <body> <div> <div id="chatsContent"></div> <div> 請輸入聊天內容:<input type="text" id="content" placeholder="聊天內容"> 目標用戶:<input type="text" id="to" placeholder="目標用戶"> <button type="button" id="send">發送</button> </div> </div> </body> </html>
var stompClient = null; //創建一個 WebSocket 鏈接,創建鏈接以前必須輸入用戶名 function connect() { //建立一個 SockeJS 實例 var socket = new SockJS('/chat'); stompClient = Stomp.over(socket); stompClient.connect({}, function (frame) { //使用 subscribe 方法訂閱服務端發送回來的消息,並將服務端發送的消息展現出來 stompClient.subscribe('/user/queue/chat', function (chat) { showGreeting(JSON.parse(chat.body)); }) }) } //發送信息 function sendMsg() { stompClient.send("/app/chat", {}, JSON.stringify({'content': $('#content').val(), 'to': $('#to').val()})); } //展現信息 function showGreeting(message) { $('#chatsContent') .append("<div>" + message.from + ":" + message.content + "</div>") } $(function () { connect(); $('#send').click(function () { sendMsg(); }); })
演示效果時請使用不一樣用戶登陸的同一瀏覽器或者不一樣瀏覽器演示