最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

SpringBoot+STOMP協(xié)議實(shí)現(xiàn)私聊、群聊

 更新時(shí)間:2021年06月10日 15:27:35   作者:叁滴水  
本文將結(jié)合實(shí)例代碼,介紹SpringBoot+STOMP協(xié)議實(shí)現(xiàn)私聊、群聊,對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧

一、為什么需要STOMP?

        WebSocket 協(xié)議是一種相當(dāng)?shù)图?jí)的協(xié)議。它定義了如何將字節(jié)流轉(zhuǎn)換為幀。幀可以包含文本或二進(jìn)制消息。由于消息本身不提供有關(guān)如何路由或處理它的任何其他信息,因此很難在不編寫其他代碼的情況下實(shí)現(xiàn)更復(fù)雜的應(yīng)用程序。幸運(yùn)的是,WebSocket 規(guī)范允許在更高的應(yīng)用程序級(jí)別上使用子協(xié)議。

        另外,單單使用WebSocket完成群聊、私聊功能時(shí),需要自己管理session信息,通過STOMP協(xié)議時(shí),spring已經(jīng)封裝好,開發(fā)者只需要關(guān)注自己的主題、訂閱關(guān)系即可。

二、STOMP詳解

消息流簡單代理

        STOMP 中文為“面向消息的簡單文本協(xié)議”,STOMP 提供了能夠協(xié)作的報(bào)文格式,以至于 STOMP 客戶端可以與任何 STOMP 消息代理(Brokers)進(jìn)行通信,從而為多語言,多平臺(tái)和 Brokers 集群提供簡單且普遍的消息協(xié)作。STOMP 協(xié)議可以建立在 WebSocket 之上,也可以建立在其他應(yīng)用層協(xié)議之上。通過 Websocket建立 STOMP 連接,也就是說在 Websocket 連接的基礎(chǔ)上再建立 STOMP 連接。最終實(shí)現(xiàn)如上圖所示,這一點(diǎn)可以在代碼中有一個(gè)良好的體現(xiàn)。

業(yè)界已經(jīng)有很多優(yōu)秀的 STOMP 的服務(wù)器/客戶端的開源實(shí)現(xiàn)

  • STOMP 服務(wù)器:ActiveMQ、RabbitMQ、StompServer、…
  • STOMP 客戶端庫:stomp.js(javascript)

        Stomp 的特點(diǎn)是客戶端的實(shí)現(xiàn)很容易,服務(wù)端相當(dāng)于消息隊(duì)列的 broker 或者是 server,一般不需要我們?nèi)?shí)現(xiàn),所以重點(diǎn)關(guān)注一下客戶端如何使用

  • CONNECT 啟動(dòng)與服務(wù)器的流或 TCP 連接
  • SEND發(fā)送消息
  • SUBSCRIBE 訂閱主題
  • UNSUBSCRIBE 取消訂閱
  • BEGIN 啟動(dòng)事物
  • COMMIT提交事物
  • ABORT回滾事物
  • ACK確認(rèn)來自訂閱的消息的消費(fèi)
  • NACK告訴服務(wù)器客戶端沒有消費(fèi)該消息
  • DISCONNECT斷開連接

        其實(shí)STOMP協(xié)議并不是為WS所設(shè)計(jì)的, 它其實(shí)是消息隊(duì)列的一種協(xié)議, 和AMQP,JMS是平級(jí)的。 只不過由于它的簡單性恰巧可以用于定義WS的消息體格式。 目前很多服務(wù)端消息隊(duì)列都已經(jīng)支持了STOMP, 比如RabbitMQ, Apache ActiveMQ等。很多語言也都有STOMP協(xié)議的客戶端解析庫,像JAVA的Gozirra,C的libstomp,Python的pyactivemq,JavaScript的stomp.js等等。

STOMP協(xié)議官方文檔

三、SpringBoot集成STOMP代碼示例

3.1、功能示例

image-20210609170607678 

image-20210609170552704 

3.2、架構(gòu)圖

image-20210609143546406

3.3、服務(wù)端代碼

pom文件引入jar

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>org.example</groupId>
    <artifactId>websocket-demo</artifactId>
    <version>1.0-SNAPSHOT</version>

    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.3.10.RELEASE</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>
    <properties>
        <java.version>1.8</java.version>
    </properties>
    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.apache.commons</groupId>
            <artifactId>commons-lang3</artifactId>
        </dependency>

        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <version>1.18.12</version>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-websocket</artifactId>
        </dependency>

        <dependency>
            <groupId>org.webjars</groupId>
            <artifactId>webjars-locator-core</artifactId>
        </dependency>
        <dependency>
            <groupId>org.webjars</groupId>
            <artifactId>sockjs-client</artifactId>
            <version>1.0.2</version>
        </dependency>
        <dependency>
            <groupId>org.webjars</groupId>
            <artifactId>stomp-websocket</artifactId>
            <version>2.3.3</version>
        </dependency>
        <dependency>
            <groupId>org.webjars</groupId>
            <artifactId>bootstrap</artifactId>
            <version>3.3.7</version>
        </dependency>
        <dependency>
            <groupId>org.webjars</groupId>
            <artifactId>jquery</artifactId>
            <version>3.1.0</version>
        </dependency>


        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>fastjson</artifactId>
            <version>1.2.62</version>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <configuration>
                    <source>1.8</source>
                    <target>1.8</target>
                </configuration>
            </plugin>
        </plugins>
    </build>
</project>

WebSocketMessageBroker配置類

@Configuration
@EnableWebSocketMessageBroker
public class WebSocketConfig implements WebSocketMessageBrokerConfigurer
{
	// 啟用一個(gè)簡單的基于內(nèi)存的消息代理
	@Override
	public void configureMessageBroker(MessageBrokerRegistry config) {
		//通過/topic 開頭的主題可以進(jìn)行訂閱
		config.enableSimpleBroker("/topic");
		//send命令時(shí)需要帶上/app前綴
		config.setApplicationDestinationPrefixes("/app");
        //修改convertAndSendToUser方法前綴, 稍后解釋作用
//		config.setUserDestinationPrefix ("/myUserPrefix");
	}
	@Override
	public void registerStompEndpoints(StompEndpointRegistry registry) {
		//連接前綴
		registry.addEndpoint("/gs-guide-websocket")
				.setAllowedOrigins("*")  // 跨域處理
				.withSockJS();  //支持socketJs
	}
}

@EnableWebSocketMessageBroker注解啟用 WebSocket 消息處理,由消息代理支持。

SockJS 有一些瀏覽器中缺少對(duì) WebSocket 的支持,而 SockJS 是一個(gè)瀏覽器的 JavaScript庫,它提供了一個(gè)類似于網(wǎng)絡(luò)的對(duì)象,SockJS 提供了一個(gè)連貫的,跨瀏覽器的JavaScriptAPI,它在瀏覽器和 Web 服務(wù)器之間創(chuàng)建了一個(gè)低延遲、全雙工、跨域通信通道。SockJS 的一大好處在于提供了瀏覽器兼容性。即優(yōu)先使用原生WebSocket,如果瀏覽器不支持 WebSocket,會(huì)自動(dòng)降為輪詢的方式。如果你使用 Java 做服務(wù)端,同時(shí)又恰好使用 Spring Framework 作為框架,那么推薦使用SockJS。

控制器代碼

@Slf4j
@RestController
public class TestController
{
	@Autowired
	private SimpMessagingTemplate simpMessagingTemplate;

	@MessageMapping("/hello")
	@SendTo ("/topic/greetings")
	public Greeting greeting(HelloMessage message) throws Exception {
		Thread.sleep(1000); // simulated delay
		return new Greeting("Hello, " + HtmlUtils.htmlEscape(message.getName()) + "!");
	}

	@MessageMapping("/topic/greetings")
	public Greeting greeting2(HelloMessage message) throws Exception {
		Thread.sleep(1000); // simulated delay
		log.info ("Hello, " + HtmlUtils.htmlEscape(message.getName()) + "!");
		return new Greeting("Hello, " + HtmlUtils.htmlEscape(message.getName()) + "!");
	}
	@GetMapping ("/hello2")
	public void greeting3(HelloMessage message) throws Exception {
		Thread.sleep(1000); // simulated delay
		simpMessagingTemplate.convertAndSend ("/topic/greetings",
				new Greeting("Hello, " + HtmlUtils.htmlEscape(message.getName()) + "!"));
	}


	@MessageMapping("/sendToUser")
	public void sendToUser(HelloMessage message) throws Exception {
		Thread.sleep(1000); // simulated delay
		log.info ("userId:{},msg:{}",message.getUserId (),message.getName ());
//		simpMessagingTemplate.convertAndSendToUser (message.getUserId (),"/sendToUser",
//				new Greeting("Hello, " + HtmlUtils.htmlEscape(message.getName()) + "!"));
//		simpMessagingTemplate.convertAndSend ("/user/1/sendToUser",
//				new Greeting("Hello, " + HtmlUtils.htmlEscape(message.getName()) + "!"));
		simpMessagingTemplate.convertAndSend ("/topic/user/"+message.getUserId ()+"/sendToUser",
				new Greeting("Hello, " + HtmlUtils.htmlEscape(message.getName()) + "!"));
	}
}
  • @MessageMapping 功能與RequestMapping注解類似。send指令發(fā)送信息時(shí)添加此注解
  • @SendTo/@SendToUser 將信息輸出到該主題??蛻舳擞嗛喭瑯拥闹黝}后就會(huì)收到信息。
  • 在只有指定@MessageMapping時(shí)@MessageMapping == “/topic” + @SendTo
  • 如果想使用rest接口發(fā)送消息。可以通過SimpMessagingTemplate進(jìn)行發(fā)送。
  • 點(diǎn)對(duì)點(diǎn)聊天時(shí),可以使用SimpMessagingTemplate.convertAndSendToUser方法發(fā)送。個(gè)人意味比注解@SendToUser更加容易理解,更加方便
  • convertAndSendToUser方法和convertAndSend類似,區(qū)別在于convertAndSendToUser方法會(huì)在主題默認(rèn)添加/user/為前綴。因此,示例代碼中convertAndSend方法直接傳入"/topic/user/"+message.getUserId ()+"/sendToUser" 也是點(diǎn)對(duì)點(diǎn)發(fā)送。topic其中是默認(rèn)前綴。
  • 如果想修改convertAndSendToUser默認(rèn)前綴可在配置類進(jìn)行配置,可在WebSocketConfig類中查看。

3.4、h5代碼

<!DOCTYPE html>
<html>
<head>
    <title>Hello WebSocket</title>
    <link href="/webjars/bootstrap/css/bootstrap.min.css" rel="external nofollow"  rel="stylesheet">
    <link href="/main.css" rel="external nofollow"  rel="stylesheet">
    <script type="text/javascript" src="https://cdn.bootcss.com/jquery/3.2.1/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>
<noscript><h2 style="color: #ff0000">Seems your browser doesn't support Javascript! Websocket relies on Javascript being
    enabled. Please enable
    Javascript and reload this page!</h2></noscript>
<div id="main-content" class="container">
    <div class="row">
        <div class="col-md-6">
            <form class="form-inline">
                <div class="form-group">
                    <label for="connect">WebSocket connection:</label>
                    <button id="connect" class="btn btn-default" type="submit">Connect</button>
                    <button id="disconnect" class="btn btn-default" type="submit" disabled="disabled">Disconnect
                    </button>
                </div>
            </form>
        </div>
        <div class="col-md-6">
            <form class="form-inline">
                <div class="form-group">
                    <label for="name">What is your name?</label>
                    <input type="text" id="name" class="form-control" placeholder="Your name here...">
                    <input type="text" id="userId" class="form-control" placeholder="userId">
                </div>
                <button id="send" class="btn btn-default" type="submit">Send</button>
                <button id="send2" class="btn btn-default" type="submit">Send2</button>

                <button id="send3" class="btn btn-default" type="submit">SendToUser</button>

            </form>
        </div>
    </div>
    <div class="row">
        <div class="col-md-12">
            <table id="conversation" class="table table-striped">
                <thead>
                <tr>
                    <th>Greetings</th>
                </tr>
                </thead>
                <tbody id="greetings">
                </tbody>
            </table>
        </div>
    </div>
</div>
</body>
</html>

app.js

var stompClient = null;
var userId = null;
function setConnected(connected) {
    $("#connect").prop("disabled", connected);
    $("#disconnect").prop("disabled", !connected);
    if (connected) {
        $("#conversation").show();
    }
    else {
        $("#conversation").hide();
    }
    $("#greetings").html("");
}

function connect() {
    var socket = new SockJS('/gs-guide-websocket');
    stompClient = Stomp.over(socket);
    stompClient.connect({}, function (frame) {
        setConnected(true);
        console.log('Connected: ' + frame);
        stompClient.subscribe('/topic/greetings', function (greeting) {
            showGreeting(JSON.parse(greeting.body).content);
        });
        //對(duì)應(yīng)controller greeting2方法  注意,這兒有兩個(gè)topic
         stompClient.subscribe('/topic/topic/greetings', function (greeting) {
                showGreeting(JSON.parse(greeting.body).content);
          });
  stompClient.subscribe('/topic/user/'+userId+'/sendToUser', function (greeting) {
                          showGreeting(JSON.parse(greeting.body).content);
                    });
           stompClient.subscribe('/user/'+userId+'/sendToUser', function (greeting) {
                          showGreeting(JSON.parse(greeting.body).content);
                    });
    });
}

function disconnect() {
    if (stompClient !== null) {
        stompClient.disconnect();
    }
    setConnected(false);
    console.log("Disconnected");
}

function sendName() {
    stompClient.send("/app/hello", {}, JSON.stringify({'name': $("#name").val()}));
//    stompClient.send("/hello", {}, JSON.stringify({'name': $("#name").val()}));
}
function sendName2() {
    stompClient.send("/app/topic/greetings", {}, JSON.stringify({'name': $("#name").val()}));
//    stompClient.send("/topic/greetings", {}, JSON.stringify({'name': $("#name").val()}));
}
function sendName3() {
    stompClient.send("/app/sendToUser", {}, JSON.stringify({'userId':$("#userId").val(),'name': $("#name").val()}));
}

function showGreeting(message) {
    $("#greetings").append("<tr><td>" + message + "</td></tr>");
}
function GetQueryString(name) {
	var reg = new RegExp("(^|&)" + name + "=([^&]*)(&|$)", "i");
	var r = window.location.search.substr(1).match(reg); //獲取url中"?"符后的字符串并正則匹配
	var context = "";
	if (r != null)
		context = r[2];
	reg = null;
	r = null;
	return context == null || context == "" || context == "undefined" ? "" : context;
}
$(function () {
    userId =  GetQueryString("userId");
    $("form").on('submit', function (e) {
        e.preventDefault();
    });
    $( "#connect" ).click(function() { connect(); });
    $( "#disconnect" ).click(function() { disconnect(); });
    $( "#send" ).click(function() { sendName(); });
    $( "#send2" ).click(function() { sendName2(); });
    $( "#send3" ).click(function() { sendName3(); });

});

一些無關(guān)緊要的類

public class Greeting
{
   private String content;

   public Greeting() {
   }

   public Greeting(String content) {
      this.content = content;
   }

   public String getContent() {
      return content;
   }
}
public class HelloMessage
{
   private String userId;
   private String name;
    // 省去get/set
}
Name3(); });

});

spring參考文檔

websocket參考文檔

到此這篇關(guān)于SpringBoot+STOMP協(xié)議實(shí)現(xiàn)私聊、群聊的文章就介紹到這了,更多相關(guān)SpringBoot STOMP私聊、群聊內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • java注解的類型知識(shí)點(diǎn)總結(jié)

    java注解的類型知識(shí)點(diǎn)總結(jié)

    在本篇文章里小編給大家整理了一篇關(guān)于java注解的類型知識(shí)點(diǎn)總結(jié)內(nèi)容,有興趣的朋友們可以學(xué)習(xí)下。
    2021-03-03
  • 詳解JavaWeb過濾器 Filter問題解決

    詳解JavaWeb過濾器 Filter問題解決

    過濾器就是對(duì)事物進(jìn)行過濾的,在Web中的過濾器,當(dāng)然就是對(duì)請(qǐng)求進(jìn)行過濾,我們使用過濾器,就可以對(duì)請(qǐng)求進(jìn)行攔截,然后做相應(yīng)的處理,實(shí)現(xiàn)許多特殊功能,今天主要給大家講解JavaWeb過濾器 Filter問題解決,感興趣的朋友一起看看吧
    2022-10-10
  • 使用jpa的時(shí)候set實(shí)體類屬性自動(dòng)持久化的解決方案

    使用jpa的時(shí)候set實(shí)體類屬性自動(dòng)持久化的解決方案

    這篇文章主要介紹了使用jpa的時(shí)候set實(shí)體類屬性自動(dòng)持久化的解決方案,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2021-08-08
  • 手把手帶你用java搞定青蛙跳臺(tái)階

    手把手帶你用java搞定青蛙跳臺(tái)階

    這篇文章主要給大家介紹了關(guān)于Java青蛙跳臺(tái)階問題的解決思路與代碼,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2021-08-08
  • Java實(shí)現(xiàn)的數(shù)組去重與排序操作詳解

    Java實(shí)現(xiàn)的數(shù)組去重與排序操作詳解

    這篇文章主要介紹了Java實(shí)現(xiàn)的數(shù)組去重與排序操作,結(jié)合實(shí)例形式分析了Java針對(duì)數(shù)組去重及排序操作相關(guān)遍歷、排序、判斷等使用技巧與注意事項(xiàng),需要的朋友可以參考下
    2018-07-07
  • SpringBoot結(jié)合SpringSecurity實(shí)現(xiàn)圖形驗(yàn)證碼功能

    SpringBoot結(jié)合SpringSecurity實(shí)現(xiàn)圖形驗(yàn)證碼功能

    這篇文章主要介紹了SpringBoot + SpringSecurity 實(shí)現(xiàn)圖形驗(yàn)證碼功能,小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧
    2018-05-05
  • Spring Bean常用的的裝配方式詳解

    Spring Bean常用的的裝配方式詳解

    這篇文章主要介紹了Spring Bean常用的的裝配方式詳解,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下
    2019-07-07
  • spring @Lazy延遲注入的邏輯實(shí)現(xiàn)

    spring @Lazy延遲注入的邏輯實(shí)現(xiàn)

    有時(shí)候我們會(huì)在屬性注入的時(shí)候添加@Lazy注解實(shí)現(xiàn)延遲注入,今天咱們通過閱讀源碼來分析下原因,感興趣的可以了解一下
    2021-08-08
  • Java基礎(chǔ)-Java變量的聲明和作用域

    Java基礎(chǔ)-Java變量的聲明和作用域

    這篇文章主要介紹了Java變量的聲明和作用域,變量其實(shí)就是內(nèi)存中的一個(gè)存儲(chǔ)空間,用來存儲(chǔ)數(shù)據(jù),具體的相關(guān)內(nèi)容,需要的小伙伴可以參考下面文章內(nèi)容
    2022-01-01
  • springboot如何忽略接收請(qǐng)求中的參數(shù)

    springboot如何忽略接收請(qǐng)求中的參數(shù)

    這篇文章主要介紹了springboot如何忽略接收請(qǐng)求中的參數(shù)問題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2024-07-07

最新評(píng)論

东光县| 若羌县| 孟村| 福贡县| 高青县| 自治县| 郓城县| 碌曲县| 五常市| 五大连池市| 台南县| 宁波市| 武乡县| 织金县| 宝丰县| 棋牌| 榆林市| 山东省| 土默特左旗| 休宁县| 阿勒泰市| 深泽县| 桑植县| 永新县| 中西区| 雷山县| 和林格尔县| 青河县| 萍乡市| 贵定县| 婺源县| 慈利县| 天等县| 禹州市| 当雄县| 渭南市| 南京市| 兴城市| 台州市| 沭阳县| 普陀区|