1. 程式人生 > >通過WebSocket和STOMP實現瀏覽器和伺服器的訊息通訊

通過WebSocket和STOMP實現瀏覽器和伺服器的訊息通訊

非同步訊息

非同步訊息有兩個重要的概念,訊息代理(broker)和目的地(destination)。


訊息代理:當一個應用傳送訊息時,需要制定傳送的目的地,然後將訊息交給訊息代理(類似郵局),訊息代理會確保訊息傳送到指定的目的地。


目的地:不同的訊息系統有不同的訊息路由模式,但是有兩種通用的目的地:佇列(queue)和主題(topic),分別對應兩種訊息模型:點對點模型和釋出/訂閱模型

點對點模型

佇列可以有多個接收者,但是訊息只能被一個接收者取走。

釋出-訂閱訊息模型


主題裡的訊息可以傳送給多個訂閱者。

WebSockt實現瀏覽器和服務端通訊

WebSocket協議提供了通過套接字實現全雙工、非同步通訊的功能。首先,瀏覽器的JavaScript客戶端建立一個socket並連線到服務端,接下來,客戶端和服務端之間可以通過這個通道傳送和接收訊息,服務端可以通過這個連結傳送更新到客戶端。

通過Spring的WebSocketAPI實現

客戶端程式碼:

<html>
  <head>
    <title>Home</title>
    <script th:src="@{/webjars/sockjs-client/0.3.4/sockjs.min.js}"></script>
    <script th:src="@{/webjars/jquery/2.0.3/jquery.min.js}"></script>
  </head>
  <body>
    <button id="stop">Stop</button>
  
    <script th:inline="javascript">
      var sock = new SockJS([[@{/marco}]]);//SockJS是對WebSocket技術的一種模擬,在瀏覽器不支援WebSocket通訊時,
      提供其他的通訊方式。
      sock.onopen = function() {
    	  console.log('Opening');
    	  sayMarco();
      }
      
      sock.onmessage = function(e) {
    	  console.log('Received message: ', e.data);
    	  $('#output').append('Received "' + e.data + '"<br/>');
    	  setTimeout(function(){sayMarco()}, 2000);
      }
      
      sock.onclose = function() {
    	  console.log('Closing');
      }
      
      function sayMarco() {
    	  console.log('Sending Marco!');
    	  $('#output').append('Sending "Marco!"<br/>');
    	  sock.send("Marco!");
      }

      $('#stop').click(function() {sock.close()});
      </script>
    
    <div id="output"></div>
  </body>
</html>

上面程式碼中,sock建立的引數是/marco,這是一個路徑,代表該客服端sock連線到服務端的路徑是當前路徑加上這個相對路徑。而這個/marco其實對應的就是服務端的handler訊息處理器。

服務端程式碼:

public class MarcoHandler extends AbstractWebSocketHandler {

	private static final Logger logger = LoggerFactory.getLogger(MarcoHandler.class);
	//處理文字訊息
	@Override
	protected void handleTextMessage(WebSocketSession session, TextMessage message) throws Exception {
		logger.info("Received message: " + message.getPayload());
		Thread.sleep(2000);
		session.sendMessage(new TextMessage("Polo!"));
	}
	
}   
@Configuration
@EnableWebSocket
public class WebSocketConfig implements WebSocketConfigurer {

	@Override
	public void registerWebSocketHandlers(WebSocketHandlerRegistry registry) {
		registry.addHandler(marcoHandler(), "/marco").withSockJS();
	}
  
	@Bean
	public MarcoHandler marcoHandler() {
		return new MarcoHandler();
	}

}

上面兩端程式碼中,@Configuration註解的是WebSocket的配置類。這個類指定了/marco對應的處理類,並且通過@Bean向Spring容器中注入了這個處理類。第一段程式碼則是MacroHandler類的實現,它繼承了AbstractWebSock抽象類,實現了處理訊息的方法。

總的來說,上述程式碼客戶端首先和一個服務端名為“/macro”的應用建立了連線。通過這條連線,客戶端給服務端傳送一條訊息“Macro”。服務端的handler會接收到這條訊息,並通過這條通道(WebSocketSession)傳送“polo”給客戶端。客戶端處理訊息的方法onmessage()又會在接收到訊息後呼叫sayMarco()。如此迴圈傳送訊息。這個傳送訊息的過程非同步的,也就是說客戶端給服務端傳送訊息後,不會等待服務端處理完成。所以說WebSocket建立的是一個非同步全雙工的通訊通道。

通過Sping的STOMP協議實現訊息機制

直接使用WebSocket(或者SockJS)類似於用TCP套接字編寫Web應用。正如HTTP為TCP添加了請求相應模型,定義了傳送內容的語義一樣,STOMP協議定義在WEBSOCKET協議之上,為其定義了訊息的語義。

STOMP的訊息格式和HTTP請求或相應的結構類似:


SEND是命令,表示傳送內容,接下來是兩條頭資訊,destination是傳送訊息的目的地,content-length是負載大小。 接下來一個空行,下面是負載內容,這裡是一個JSON字串。

前端程式碼:

<html>
  <head>
    <title>Home</title>
    <script th:src="@{/webjars/sockjs-client/0.3.4/sockjs.min.js}"></script>
    <script th:src="@{/webjars/stomp-websocket/2.3.0/stomp.min.js}"></script>
    <script th:src="@{/webjars/jquery/2.0.3/jquery.min.js}"></script>
  </head>
  <body>
    <button id="stop">Stop</button>
  
    <script th:inline="javascript">
      var sock = new SockJS([[@{/marcopolo}]]);
      var stomp = Stomp.over(sock);

      stomp.connect('guest', 'guest', function(frame) {
        console.log('*****  Connected  *****');
        
        stomp.subscribe("/topic/marco", handlePolo);
        sayMarco();
      });
      
      function handleOneTime(message) {
        console.log('Received: ', message);
      }

      function handlePolo(message) {
        console.log('Received: ', message);
        $('#output').append("<b>Received: " + 
            JSON.parse(message.body).message + "</b><br/>")
        if (JSON.parse(message.body).message === 'Polo!') {
          setTimeout(function(){sayMarco()}, 2000);
        }
      }
      
      function handleErrors(message) {
        console.log('RECEIVED ERROR: ', message);
        $('#output').append("<b>GOT AN ERROR!!!: " + 
            JSON.parse(message.body).message + "</b><br/>")
      }

      function sayMarco() {
        console.log('Sending Marco!');
        stomp.send("/app/marco", {}, 
            JSON.stringify({ 'message': 'Marco!' }));
        $('#output').append("<b>Send: Marco!</b><br/>")
      }

      $('#stop').click(function() {sock.close()});
      </script>
    
    <div id="output"></div>
  </body>
</html>
      var sock = new SockJS([[@{/marcopolo}]]);

      var stomp = Stomp.over(sock);

    建立了到/marcopolo的WebSocket的連線之後,呼叫stomp.min.js庫的Stomp物件的over()函式,在sock之上建立stomp客戶端,實際是封裝了SockJS。接下來建立stomp連線,訂閱一個目的地/topic/macro,並把接收道德訊息交給 handlepolo函式去處理。

服務端程式碼:

配置類:

@Configuration
@EnableWebSocketMessageBroker
public class WebSocketStompConfig extends AbstractWebSocketMessageBrokerConfigurer {

  @Override
  public void registerStompEndpoints(StompEndpointRegistry registry) {
    registry.addEndpoint("/marcopolo").withSockJS();
  }

  @Override
  public void configureMessageBroker(MessageBrokerRegistry registry) {
//    registry.enableStompBrokerRelay("/queue", "/topic");
    registry.enableSimpleBroker("/queue", "/topic");
    registry.setApplicationDestinationPrefixes("/app");
  }

controller:

@Controller
public class MarcoController {

  private static final Logger logger = LoggerFactory
      .getLogger(MarcoController.class);

  @MessageMapping("/marco")
  public Shout handleShout(Shout incoming) {
    logger.info("Received message: " + incoming.getMessage());

    try { Thread.sleep(2000); } catch (InterruptedException e) {}
    
    Shout outgoing = new Shout();
    outgoing.setMessage("Polo!");
    
    return outgoing;
  }

}

可以看到,與前端對應,配置類中註冊了endpoint:/marcopolo, 此外,在配置訊息代理的函式中,啟用了簡單代理,簡單代理會接收目的地字首為/topic和/queue的訊息,而目的地字首為/app的訊息不會交給代理,而是直接路由到帶有@MessageMapping註解的控制器方法中。這個方法的返回值會經過訊息代理髮送到目的地:


傳送訊息到客戶端

Spring提供了兩種方式傳送訊息到客戶端

  • 作為處理訊息的附帶結果
  • 使用訊息模板SimpleMessagingTemplate

上面的服務端程式碼就是講訊息作為附帶結果返回,返回的目的地是處理方法目的地預設是/macro加上一個/topic字首,也正是前端subscribe()訂閱的目的地。另外還可以通過@sendTo註解來過載目的地,相應的前端要訂閱這個過載的目的地才能收到訊息。

SimpleMessagingTemplate可以在應用的任意地方傳送訊息,不用以接收一條訊息為前提,同時可以發給指定的使用者,基於SpringSecurity。

@Service
public class SpittleFeedServiceImpl implements SpittleFeedService {

	private SimpMessagingTemplate messaging;
	private Pattern pattern = Pattern.compile("\\@(\\S+)");
	
	@Autowired
	public SpittleFeedServiceImpl(SimpMessagingTemplate messaging) {
		this.messaging = messaging;
	}
	
	public void broadcastSpittle(Spittle spittle) {
		messaging.convertAndSend("/topic/spittlefeed", spittle);
		
		Matcher matcher = pattern.matcher(spittle.getMessage());
		if (matcher.find()) {
			String username = matcher.group(1);
			messaging.convertAndSendToUser(username, "/queue/notifications",
					new Notification("You just got mentioned!"));
		}
	}
	
}

相關推薦

通過WebSocketSTOMP實現瀏覽器伺服器訊息通訊

非同步訊息非同步訊息有兩個重要的概念,訊息代理(broker)和目的地(destination)。訊息代理:當一個應用傳送訊息時,需要制定傳送的目的地,然後將訊息交給訊息代理(類似郵局),訊息代理會確保訊息傳送到指定的目的地。目的地:不同的訊息系統有不同的訊息路由模式,但是有

Spring boot:WebSocket+SockJS+Stomp實現廣播點對點訊息傳送

筆記 廣播式 STS工具新建spring boot專案 使用Thymeleaf和Websocket依賴 pom.xml: <?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://mave

第18章-使用WebSocketSTOMP實現消息功能

control rom rup mpm 空間 except 線路 如何 cto Spring 4.0為WebSocket通信提供了支持,包括: 發送和接收消息的低層級API; 發送和接收消息的高級API; 用來發送消息的模板; 支持SockJS,用來解決瀏覽器端、服務器

Spring 學習——基於Spring WebSocket STOMP實現簡單的聊天功能

本篇主要講解如何使用Spring websocket 和STOMP搭建一個簡單的聊天功能專案,裡面使用到的技術,如websocket和STOMP等會簡單介紹,不會太深,如果對相關介紹不是很瞭解的,請自行查閱相關知識。 本篇的專案主要是一個學習Spring we

Java後臺框架篇--spring websocket stomp實現訊息功能

<%@ taglib prefix="spring" uri="http://www.springframework.org/tags" %> <html> <head> <title>Home</title> <spring:

第18章 使用WebSocket STOMP實現訊息功能

概述: 瀏覽器和伺服器之間傳送訊息在SpringMVC控制器中處理訊息為目標使用者傳送訊息為了解決應用為web應用之間的通訊 Spring4.0 為 WebSocket通訊提供了支援 包括: 傳送和接收訊息的低層級API;傳送和接收訊息的高階API;用來發送訊息的模板

SocketLog-微信除錯、API除錯AJAX的除錯的工具,能將日誌通過WebSocket輸出到Chrome瀏覽器的console中

說明 SocketLog適合Ajax除錯和API除錯, 舉一個常見的場景,用SocketLog來做微信除錯, 我們在做微信API開發的時候,如果API有bug,微信只提示“改公眾賬號暫時無法提供服務,請稍候再試” ,我們根本不知道API出來什麼問題。 有了Sock

使用spring websocket stomp實現訊息功能

<%@ taglib prefix="spring" uri="http://www.springframework.org/tags" %> <html> <head> <title>Home</title> <spring:

spring 使用WebSocket STOMP 實現訊息功能

1)本文旨在 介紹如何 利用 WebSocket 和 STOMP 實現訊息功能; 2)要知道, WebSocket 是傳送和接收訊息的 底層API,而SockJS 是在 WebSocket 之上的 API;最後 STOMP(面向訊息的簡單文字協議)是基於 SockJS 的高

libeventlibcurl實現httphttps伺服器 cJSON使用

  前言   libevent和libcurl都是功能強大的開源庫;libevent主要實現伺服器,包含了select、epoll等高併發的實現;libcurl實現了curl命令的API封裝,主要作為客戶端。這兩個開源庫的安裝可以參考我的這篇部落格:https://www.cnblogs.com/liudw

Android通過Runtime.getRuntime().exec實現PingTraceroute命令時readLine阻塞問題解決

在PC上呼叫cmd,進行一些常用的命令操作,在Android上的是通過Runtime.getRuntime().exec來執行底層Linux下的程式或指令碼(bat)。 首先連線上真機,電腦開啟CMD,輸入adb-shell,確保你要進行的指令碼語言是可以執行的。(比如

Spring Websocket+SockJS+STOMP實現即時通訊(零)—— 要點

Spring Websocket+SockJS+STOMP 實現即時通訊(一)—— 疑問解答 Spring Websocket+SockJS+STOMP 實現即時通訊(二)—— 簡單的訊息代理 Spr

Spring Websocket+SockJS+STOMP 實現即時通訊(三)—— ChannelInterceptor與ExecutorChannelInterceptor

ChannelInterceptor: Message被髮送到執行緒池,在傳送動作執行前(後)攔截,發生在當前執行緒。 ExecutorChannelInterceptor: Message被髮送到執行緒池後,線上程池持有的新執行緒中,在Message

Spring Websocket+SockJS+STOMP 實現即時通訊(四)—— MessageChannel

兩種MessageChannel實現 TemporaryReplyChannel 用於接收單個回覆訊息的臨時通道。在整個斷點除錯過程中沒有追蹤到,所以在這裡不詳細說明。 ExecutorSubscribableChannel 正如字面上所表示的這樣Ex

用ReentrantLockCondition實現生產者消費者模式 wait、notify應用場景(生產者-消費者模式)

前面一篇文章《wait、notify應用場景(生產者-消費者模式)》是一種生產者消費者模式實現,今晚這是Lock方式實現,下面是原始碼: 生產者程式碼: /** * 生產者 * * @author tangquanbin * @date 2018/12/18 22:10 */ public

Spring Websocket+SockJS+STOMP 實現即時通訊(六)—— SubProtocolWebSocketHandler

目錄 WebsocketHandler SubProtocolWebSocketHandler 四個重要成員變數 protocolHandlerLookup 子協議

Spring Websocket+SockJS+STOMP 實現即時通訊(四)—— MessageHandler

目錄 MessageHandler的作用 MessageHandler實現類 兩類MessageHandler有什麼區別? MessageHandler的作用 上一節中我們提到過,E

Spring Websocket+SockJS+STOMP 實現即時通訊(五)—— UserRegistryMessageHandler與NoOpMessageHandler

目錄 UserRegistryMessageHandler 處理來自其它應用服務的登錄檔廣播 定期廣播本地登錄檔 使用場景 使用條件 啟用配

Android手機客戶端通過JSP實現與Tomcat伺服器通訊(Msql資料庫,Json作為載體)--服務端程式碼

伺服器端主要程式碼: 1.首先構建一個Person類,用來儲存使用者資訊 public class Person private String name; private String address; private Integer age; public P

HashTableHashMap實現原理區別

HashTable和HashMap都是map介面的實現類,這兩個類的實現原理基本是一致的,都是基於陣列加連結串列的資料結構 一、實現原理: HashTable和HashMap都實現了map介面,只是HashTable繼承了Dictionary抽象類而HashMap繼承了A