Spring Boot整合WebSocket
在HTTP協(xié)議中,所有的請(qǐng)求都是由客戶端發(fā)起的,由服務(wù)端進(jìn)行響應(yīng),服務(wù)端無(wú)法向客戶端推送消息,但是在一些需要即時(shí)通信的應(yīng)用中,又不可避免地需要服務(wù)端向客戶端推送消息,傳統(tǒng)的解決方案主要有如下幾種。
1. 為什么需要WebSocket
- 輪詢
輪詢是最簡(jiǎn)單的一種解決方案,所謂輪詢,就是客戶端在固定的時(shí)間間隔下不停地向服務(wù)端發(fā)送請(qǐng)求,查看服務(wù)端是否有最新的數(shù)據(jù),若服務(wù)端有最新的數(shù)據(jù),則返回給客戶端,若服務(wù)端沒(méi)有,則返回一個(gè)空的JSON或者XML文檔。輪詢對(duì)開發(fā)人員而言實(shí)現(xiàn)方便,但是弊端也很明顯:客戶端每次都要新建HTTP請(qǐng)求,服務(wù)端要處理大量的無(wú)效請(qǐng)求,在高并發(fā)場(chǎng)景下會(huì)嚴(yán)重拖慢服務(wù)端的運(yùn)行效率,同時(shí)服務(wù)端的資源被極大的浪費(fèi)了,因此這種方式并不可取。
- 長(zhǎng)輪詢
長(zhǎng)輪詢是傳統(tǒng)輪詢的升級(jí)版,當(dāng)聰明的工程師看到輪詢所存在的問(wèn)題后,就開始解決問(wèn)題,于是有了長(zhǎng)輪詢。不同于傳統(tǒng)輪詢,在長(zhǎng)輪詢中,服務(wù)端不是每次都會(huì)立即響應(yīng)客戶端的請(qǐng)求,只有在服務(wù)端有最新數(shù)據(jù)的時(shí)候才會(huì)立即響應(yīng)客戶端的請(qǐng)求,否則服務(wù)端會(huì)持有這個(gè)請(qǐng)求而不返回,直到有新數(shù)據(jù)時(shí)才返回。這種方式可以在一定程度上節(jié)省網(wǎng)絡(luò)資源和服務(wù)器資源,但是也存在一些問(wèn)題,例如:
- 如果瀏覽器在服務(wù)器響應(yīng)之前有新數(shù)據(jù)要發(fā)送,就只能創(chuàng)建-一個(gè)新的并發(fā)請(qǐng)求,或者先嘗試斷掉當(dāng)前請(qǐng)求,再創(chuàng)建新的請(qǐng)求。
- TCP和HTTP規(guī)范中都有連接超時(shí)一說(shuō),所以所謂的長(zhǎng)輪詢并不能一直持續(xù), 服務(wù)端和客戶端的連接需要定期的連接和關(guān)閉再連接,這又增大了程序員的工作量,當(dāng)然也有一些技術(shù)能夠延長(zhǎng)每次連接的時(shí)間,但畢竟是非主流解決方案。
- Applet和Flash
Applet和Flash都已經(jīng)是明日黃花,不過(guò)在這兩個(gè)技術(shù)存在的歲月里,除了可以讓我們的HTML頁(yè)面更加絢麗之外,還可以解決消息推送問(wèn)題。開發(fā)者可以使用Applet和Flash來(lái)模擬全雙工通信,通過(guò)創(chuàng)建一一個(gè)只有 1個(gè)像素點(diǎn)大小的透明的Applet或者Flash,然后將之內(nèi)嵌在網(wǎng)頁(yè)中,再?gòu)腁pplet或者Flash的代碼中創(chuàng)建一一個(gè)Socket連接進(jìn)行雙向通信。這種連接方式消除了HTTP協(xié)議中的諸多限制,當(dāng)服務(wù)器有消息發(fā)送到客戶端的時(shí)候,開發(fā)者可以在Applet或者Flash中調(diào)用JavaScript函數(shù)將數(shù)據(jù)顯示在頁(yè)面上,當(dāng)瀏覽器有數(shù)據(jù)要發(fā)送給服務(wù)器時(shí)也一樣,通過(guò)Applet或者Flash來(lái)傳遞。這種方式真正地實(shí)現(xiàn)了全雙工通信,不過(guò)也有問(wèn)題,說(shuō)明如下:
- 瀏覽器必須能夠運(yùn)行Java或者Flash。
- 無(wú)論是Applet還是Flash 都存在安全問(wèn)題。
- 隨著HTML 5標(biāo)準(zhǔn)被各瀏覽器廠商廣泛支持,F(xiàn)lash 下架已經(jīng)被提上日程( Adobe宣布2020年正式停止支持Flash)。
其實(shí),傳統(tǒng)的解決方案不止這三種,但是無(wú)論哪種解決方案都有自身的缺陷,于是有了WebSocket。
2. WebSocket簡(jiǎn)介
WebSocket是一種在單個(gè)TCP連接.上進(jìn)行全雙工通信的協(xié)議,已被W3C定為標(biāo)準(zhǔn)。使用WebSocket可以使得客戶端和服務(wù)器之間的數(shù)據(jù)交換變得更加簡(jiǎn)單,它允許服務(wù)端主動(dòng)向客戶端推送數(shù)據(jù)。在WebSocket協(xié)議中,瀏覽器和服務(wù)器只需要完成一次握手,兩者之間就可以直接創(chuàng)建持久性的連接,并進(jìn)行雙向數(shù)據(jù)傳輸。
WebSocket使用了HTTP/1.1的協(xié)議升級(jí)特性,一個(gè)WebSocket請(qǐng)求首先使用非正常的HTTP請(qǐng)求以特定的模式訪問(wèn)一個(gè)URL,這個(gè)URL有兩種模式,分別是ws和wss,對(duì)應(yīng)HTTP協(xié)議中的HTTP和HTTPS,在請(qǐng)求頭中有一個(gè)Connection:Upgrade字段,表示客戶端想要對(duì)協(xié)議進(jìn)行升級(jí),另外還有一個(gè)Upgrade:websocket字段,表示客戶端想要將請(qǐng)求協(xié)議升級(jí)為WebSocket協(xié)議。這兩個(gè)字段共同告訴服務(wù)器要將連接升級(jí)為WebSocket這樣一種全雙工協(xié)議,如果服務(wù)端同意協(xié)議升級(jí),那么在握手完成之后,文本消息或者其他二進(jìn)制消息就可以同時(shí)在兩個(gè)方向上進(jìn)行發(fā)送,而不需要關(guān)閉和重建連接。此時(shí)的客戶端和服務(wù)端關(guān)系是對(duì)等的,它們可以互相向?qū)Ψ街鲃?dòng)發(fā)送消息。和傳統(tǒng)的解決方案相比,WebSocket主要有如下特點(diǎn):
- WebSocket使用時(shí)需要先創(chuàng)建連接,這使得WebSocket成為一種有狀態(tài)的協(xié)議,在之后的通信過(guò)程中可以省略部分狀態(tài)信息( 例如身份認(rèn)證等)。
- WebSocket連接在端口80 (ws)或者443 (wss)上創(chuàng)建,與HTTP使用的端口相同,這樣,基本上所有的防火墻都不會(huì)阻止WebSocket連接。
- WebSocket使用HTTP協(xié)議進(jìn)行握手,因此它可以自然而然地集成到網(wǎng)絡(luò)瀏覽器和HTTP服務(wù)器中,而不需要額外的成本。
- 心跳消息(ping 和pong)將被反復(fù)的發(fā)送,進(jìn)而保持WebSocket連接一直處于活躍狀態(tài)。
- 使用該協(xié)議,當(dāng)消息啟動(dòng)或者到達(dá)的時(shí)候,服務(wù)端和客戶端都可以知道。
- WebSocket連接關(guān)閉時(shí)將發(fā)送一個(gè)特殊的關(guān)閉消息。
- WebSocket支持跨域,可以避免Ajax的限制。
- HTTP規(guī)范要求瀏覽器將并發(fā)連接數(shù)限制為每個(gè)主機(jī)名兩個(gè)連接,但是當(dāng)我們使用WebSocket的時(shí)候,當(dāng)握手完成之后,該限制就不存在了,因?yàn)榇藭r(shí)的連接已經(jīng)不再是HTTP連接了。
- WebSocket協(xié)議支持?jǐn)U展,用戶可以擴(kuò)展協(xié)議,實(shí)現(xiàn)部分自定義的子協(xié)議。
- 更好的二進(jìn)制支持以及更好的壓縮效果。
WebSocket既然具有這么多優(yōu)勢(shì),使用場(chǎng)景當(dāng)然也是非常廣泛的,例如:
- 在線股票網(wǎng)站。
- 即時(shí)聊天。
- 多人在線游戲。
- 應(yīng)用集群通信。
- 系統(tǒng)性能實(shí)時(shí)監(jiān)控。
…
在了解了這么多WebSocket的基本信息后,接下來(lái)看看在Spring Boot中如何使用WebSocket。
3. Spring Boot整合WebSocket
Spring Boot 對(duì)WebSocket 提供了非常友好的支持,可以方便開發(fā)者在項(xiàng)目中快速集成WebSocket功能,實(shí)現(xiàn)單聊或者群聊。
3.1 消息群發(fā)
3.1.1 創(chuàng)建項(xiàng)目
首先創(chuàng)建一個(gè)Spring Boot項(xiàng)目,添加如下依賴:
<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.1.2</version>
</dependency>
<dependency>
<groupId>org.webjars</groupId>
<artifactId>stomp-websocket</artifactId>
<version>2.3.4</version>
</dependency>
<dependency>
<groupId>org.webjars</groupId>
<artifactId>jquery</artifactId>
<version>3.6.0</version>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
spring-bot-starter-websocket依賴是Web Socket相關(guān)依賴,其他的都是前端庫(kù),使用jar包的形式對(duì)這些前端庫(kù)進(jìn)行統(tǒng)一管理, 使用webjar添加到項(xiàng)目中的前端庫(kù),在Spring Boot項(xiàng)目中已經(jīng)默認(rèn)添加了靜態(tài)資源過(guò)濾,因此可以直接使用。
3.1.2 配置WebSocket
Spring框架提供了基于WebSocket的STOMP支持,STOMP是一個(gè)簡(jiǎn)單的可互操作的協(xié)議,
通常被用于通過(guò)中間服務(wù)器在客戶端之間進(jìn)行異步消息傳遞。WebSocket 配置如下:
@Configuration
@EnableWebSocketMessageBroker
public class WebSocketConfig implements WebSocketMessageBrokerConfigurer {
@Override
public void configureMessageBroker(MessageBrokerRegistry config) {
// 設(shè)置消息代理的前綴,如果消息的前綴為"/topic",就會(huì)將消息轉(zhuǎn)發(fā)給消息代理(broker)
// 再由消息代理廣播給當(dāng)前連接的客戶端
config.enableSimpleBroker("/topic");
// 下面方法可以配置一個(gè)或多個(gè)前綴,通過(guò)這些前綴過(guò)濾出需要被注解方法處理的消息。
// 例如這里表示前綴為"/app"的destination可以通過(guò)@MessageMapping注解的方法處理
// 而其他 destination(例如"/topic""/queue")將被直接交給 broker 處理
config.setApplicationDestinationPrefixes("/app");
}
@Override
public void registerStompEndpoints(StompEndpointRegistry registry) {
// 定義一個(gè)前綴為"/chart"的endpoint,并開啟 sockjs 支持。
// sockjs 可以解決瀏覽器對(duì)WebSocket的兼容性問(wèn)題,客戶端將通過(guò)這里配置的URL建立WebSocket連接
registry.addEndpoint("/chat").withSockJS();
}
}
代碼解釋:
- 自定義類WebSocketConfig 繼承自WebSocketMessageBrokerConfigurer 進(jìn)行WebSocket配置,然后通過(guò)@EnableWebSocketMessageBroker注解開啟WebSocket消息代理。
- config.enableSimpleBroker(“/topic”)表示設(shè)置消息代理的前綴,即如果消息的前綴是“/topic" ,就會(huì)將消息轉(zhuǎn)發(fā)給消息代理( broker),再由消息代理將消息廣播給當(dāng)前連接的客戶端。
- config.setApplicationDestinationPrefixes(“/app”)表示配置一個(gè)或多個(gè)前綴,通過(guò)這些前綴過(guò)濾出需要被注解方法處理的消息。例如,前綴為“/app”的destination可以通過(guò)@MessageMapping注解的方法處理,而其他destination(例如“/topic”“/queue”)將被直接交給broker處理。
- regitry.addEndpoint(“/chat”).withSockJS()則表示定義一個(gè)前綴為“/chat” 的endPoint,并開啟sockjs支持,sockjs 可以解決瀏覽器對(duì)WebSocket的兼容性問(wèn)題,客戶端將通過(guò)這里配置的URL來(lái)建立WebSocket連接。
3.1.3 定義Controller
定義一個(gè)Controller用來(lái)實(shí)現(xiàn)對(duì)消息的處理,代碼如下:
@Controller
public class GreetingController {
@MessageMapping("/hello")
@SendTo("/topic/greetings")
public Message greeting(Message message) throws Exception {
return message;
}
}
自定義的Message對(duì)象代碼如下:
@Data
public class Message {
private String name;
private String content;
}
根據(jù)第2步的配置,@MessageMapping(“hello”)注解的方法將用來(lái)接收“/apphello"路徑發(fā)送來(lái)的消息,在注解方法中對(duì)消息進(jìn)行處理后,再將消息轉(zhuǎn)發(fā)到@SendTo定義的路徑上,而@SendTo路徑是一個(gè)前綴為“/topic” 的路徑,因此該消息將被交給消息代理broker,再由broker 進(jìn)行廣播。
3.1.4 構(gòu)建聊天頁(yè)面
在resources/static目錄下創(chuàng)建chat.html 頁(yè)面作為聊天頁(yè)面,代碼如下:
<!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>
var stompClient = null;
// 根據(jù)是否已連接設(shè)置頁(yè)面元素狀態(tài)
function setConnected(connected) {
$("#connect").prop("disabled", connected);
$("#disconnect").prop("disabled", !connected);
if (connected) {
$("#conversation").show();
$("#chat").show();
}
else {
$("#conversation").hide();
$("#chat").hide();
}
$("#greetings").html("");
}
// 建立一個(gè)WebSocket連接
function connect() {
// 用戶名不能為空
if (!$("#name").val()) {
return;
}
// 首先使用 SockJS 建立連接
var socket = new SockJS("/chat");
// 然后創(chuàng)建一個(gè)STOMP實(shí)例發(fā)起連接請(qǐng)求
stompClient = Stomp.over(socket);
// 連接成功回調(diào)
stompClient.connect({}, function (frame) {
// 進(jìn)行頁(yè)面設(shè)置
setConnected(true);
// 訂閱服務(wù)端發(fā)送回來(lái)的消息
stompClient.subscribe('/topic/greetings', function (greeting) {
// 將服務(wù)端發(fā)送回來(lái)的消息展示出來(lái)
showGreeting(JSON.parse(greeting.body));
});
});
}
// 斷開WebSocket連接
function disconnect() {
if (stompClient !== null) {
stompClient.disconnect();
}
setConnected(false);
}
// 發(fā)送消息
function sendName() {
stompClient.send("/app/hello",
{},
JSON.stringify({'name': $("#name").val(),'content':$("#content").val()}));
}
// 將服務(wù)端發(fā)送回來(lái)的消息展示出來(lái)
function showGreeting(message) {
$("#greetings")
.append("<div>" + message.name+":"+message.content + "</div>");
}
// 頁(yè)面加載后進(jìn)行初始化動(dòng)作
$(function () {
$( "#connect" ).click(function() { connect(); });
$( "#disconnect" ).click(function() { disconnect(); });
$( "#send" ).click(function() { sendName(); });
});
</script>
</head>
<body>
<div>
<label for="name">請(qǐng)輸入用戶名:</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">請(qǐng)輸入聊天內(nèi)容:</label>
<input type="text" id="content" placeholder="聊天內(nèi)容">
</div>
<button id="send" type="button">發(fā)送</button>
<div id="greetings">
<div id="conversation" style="display: none">群聊進(jìn)行中...</div>
</div>
</div>
</body>
</html>
代碼解釋:
- connect 方法表示建立一個(gè)WebSocket連接,在建立WebSocket連接時(shí),用戶必須先輸入用戶名,然后才能建立連接。
- 第19~26行首先使用SockJS建立連接,然后創(chuàng)建一個(gè)STOMP實(shí)例發(fā)起連接請(qǐng)求,在連接成功的回調(diào)方法中,首先調(diào)用setConnected(true);方 法進(jìn)行頁(yè)面的設(shè)置,然后調(diào)用STOMP中的subscribe方法訂閱服務(wù)端發(fā)送回來(lái)的消息,并將服務(wù)端發(fā)送來(lái)的消息展示出來(lái)(使用showGreeting方法)。
- 調(diào)用STOMP中的disconnect 方法可以斷開一個(gè)WebSocket連接。
3.1.5 測(cè)試
接下來(lái)啟動(dòng)Spring Boot 項(xiàng)目進(jìn)行測(cè)試,在瀏覽器中輸入htp:/ocalhost:8080/chat.html,顯示結(jié)果如圖所示。
用戶首先輸入用戶名,然后單擊“連接”按鈕,結(jié)果如圖所示。
然后換一個(gè)瀏覽器,或者使用Chrome瀏覽器的多用戶(注意不是多窗口),重復(fù)剛才的步驟,這樣就有兩個(gè)用戶連接上了,接下來(lái)就可以開始群聊了(當(dāng)然也可以有更多的用戶連接上來(lái)),如圖所示。
3.2 消息點(diǎn)對(duì)點(diǎn)發(fā)送
在3.1小節(jié)中介紹的消息發(fā)送使用到了@SendTo注解,該注解將方法處理過(guò)的消息轉(zhuǎn)發(fā)到broker,再由broker進(jìn)行消息廣播。除了@SendTo注解外, Spring 還提供了SimpMessagingTemplate類來(lái)讓開發(fā)者更加靈活地發(fā)送消息。
3.2.1 添加依賴
既然是點(diǎn)對(duì)點(diǎn)發(fā)送,就應(yīng)該有用戶的概念,因此,首先在項(xiàng)目中加入Spring Security的依賴,代碼如下:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
3.2.2 配置Spring Security
對(duì)Spring Security進(jìn)行配置,添加兩個(gè)用戶,同時(shí)配置所有地址都認(rèn)證后才能訪問(wèn),代碼如下:
@Configuration
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
// 指定密碼的加密方式
@SuppressWarnings("deprecation")
@Bean
PasswordEncoder passwordEncoder(){
// 不對(duì)密碼進(jìn)行加密
return NoOpPasswordEncoder.getInstance();
}
// 配置用戶及其對(duì)應(yīng)的角色
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.inMemoryAuthentication()
.withUser("admin").password("123").roles("ADMIN","USER")
.and()
.withUser("suohe").password("123").roles("USER");
}
// 配置 URL 訪問(wèn)權(quán)限
@Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests() // 開啟 HttpSecurity 配置
.anyRequest().authenticated() // 用戶訪問(wèn)所有地址都必須登錄認(rèn)證后訪問(wèn)
.and().formLogin().permitAll(); // 開啟表單登錄
}
}
這里就是Spring Security的一個(gè)常規(guī)配置,相關(guān)配置含義可以參考我前面文章。
3.2.3 改造WebSocket配置
接下來(lái)對(duì)WebSocket配置進(jìn)行改造,代碼如下:
@Configuration
@EnableWebSocketMessageBroker
public class WebSocketConfig implements WebSocketMessageBrokerConfigurer {
@Override
public void configureMessageBroker(MessageBrokerRegistry config) {
// 設(shè)置消息代理的前綴,如果消息的前綴為"/topic"、"/queue",就會(huì)將消息轉(zhuǎn)發(fā)給消息代理(broker)
// 再由消息代理廣播給當(dāng)前連接的客戶端
config.enableSimpleBroker("/topic","/queue");
// 下面方法可以配置一個(gè)或多個(gè)前綴,通過(guò)這些前綴過(guò)濾出需要被注解方法處理的消息。
// 例如這里表示前綴為"/app"的destination可以通過(guò)@MessageMapping注解的方法處理
// 而其他 destination(例如"/topic""/queue")將被直接交給 broker 處理
config.setApplicationDestinationPrefixes("/app");
}
@Override
public void registerStompEndpoints(StompEndpointRegistry registry) {
// 定義一個(gè)前綴為"/chart"的endpoint,并開啟 sockjs 支持。
// sockjs 可以解決瀏覽器對(duì)WebSocket的兼容性問(wèn)題,客戶端將通過(guò)這里配置的URL建立WebSocket連接
registry.addEndpoint("/chat").withSockJS();
}
}
這里的修改是在config.enableSimpleBroker(“/topic”);方法的基礎(chǔ) 上又增加了一個(gè)broker 前綴“/queue”,方便對(duì)群發(fā)消息和點(diǎn)對(duì)點(diǎn)消息進(jìn)行管理。
3.2.4 配置Controller
對(duì)WebSocket的Controller 進(jìn)行改造,代碼如下:
@Controller
public class GreetingController {
@Autowired
SimpMessagingTemplate messagingTemplate;
// 處理來(lái)自"/app/hello"路徑的消息
@MessageMapping("/hello")
@SendTo("/topic/greetings")
public Message greeting(Message message) throws Exception {
return message;
}
// 處理來(lái)自"/app/chat"路徑的消息
@MessageMapping("/chat")
public void chat(Principal principal, Chat chat) {
// 獲取當(dāng)前登錄用戶的用戶名
String from = principal.getName();
// 將用戶設(shè)置給chat對(duì)象的from屬性
chat.setFrom(from);
// 再將消息發(fā)送出去,發(fā)送的目標(biāo)用戶就是 chat 對(duì)象的to屬性值
messagingTemplate.convertAndSendToUser(chat.getTo(),
"/queue/chat", chat);
}
}
代碼解釋:
-
群發(fā)消息依然使用@SendTo注解來(lái)實(shí)現(xiàn),點(diǎn)對(duì)點(diǎn)的消息發(fā)送則使用SimpMessagingTemplate來(lái)實(shí)現(xiàn)。
-
第10~16 行定義了一個(gè)新的消息處理接口,@MessageMapping("/chat)注 解表示來(lái)自“/app/chat”路徑的消息將被chat 方法處理。chat 方法的第一個(gè)參數(shù)Principal可以用來(lái)獲取當(dāng)前登錄用戶的信息,第二個(gè)參數(shù)則是客戶端發(fā)送來(lái)的消息。
-
在chat 方法中,首先獲取當(dāng)前用戶的用戶名,設(shè)置給chat對(duì)象的from屬性,再將消息發(fā)送出去,發(fā)送的目標(biāo)用戶就是chat對(duì)象的to屬性值。
-
消息發(fā)送使用的方法是convertAndSendToUser,該方法內(nèi)部調(diào)用了convertAndSend方法,并對(duì)消息路徑做了處理,部分源碼如下:
public void convertAndSendToUser(String user, String destination, Object payload) throws MessagingException { this.convertAndSendToUser(user, destination, payload, (MessagePostProcessor)null); } public void convertAndSendToUser(String user, String destination, Object payload) throws MessagingException { this.convertAndSendToUser(user, destination, payload, (MessagePostProcessor)null); }//這里destinationPrefix 的默認(rèn)值是“/user”, 也就是說(shuō)消息的最終發(fā)送路徑是“/user/用戶名/queue/chat"。
-
chat是一個(gè)普通的JavaBean, to 屬性表示消息的目標(biāo)用戶,from 表示消息從哪里來(lái),content則是消息的主體內(nèi)容。
3.2.5 創(chuàng)建在線聊天頁(yè)面
在resources/static 目錄下創(chuàng)chat.html 頁(yè)面作為在線聊天頁(yè)面,代碼如下:
<!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>
var stompClient = null;
// 建立一個(gè)WebSocket連接
function connect() {
// 首先使用 SockJS 建立連接
var socket = new SockJS('/chat');
// 然后創(chuàng)建一個(gè)STOMP實(shí)例發(fā)起連接請(qǐng)求
stompClient = Stomp.over(socket);
// 連接成功回調(diào)
stompClient.connect({}, function (frame) {
// 訂閱服務(wù)端發(fā)送回來(lái)的消息
stompClient.subscribe('/user/queue/chat', function (chat) {
// 將服務(wù)端發(fā)送回來(lái)的消息展示出來(lái)
showGreeting(JSON.parse(chat.body));
});
});
}
// 發(fā)送消息
function sendMsg() {
stompClient.send("/app/chat", {},
JSON.stringify({'content':$("#content").val(),
'to':$("#to").val()}));
}
// 將服務(wù)端發(fā)送回來(lái)的消息展示出來(lái)
function showGreeting(message) {
$("#chatsContent")
.append("<div>" + message.from+":"+message.content + "</div>");
}
// 頁(yè)面加載后進(jìn)行初始化動(dòng)作
$(function () {
// 頁(yè)面加載完畢后自動(dòng)連接
connect();
$( "#send" ).click(function() { sendMsg(); });
});
</script>
</head>
<body>
<div id="chat">
<div id="chatsContent">
</div>
<div>
請(qǐng)輸入聊天內(nèi)容:
<input type="text" id="content" placeholder="聊天內(nèi)容">
目標(biāo)用戶:
<input type="text" id="to" placeholder="目標(biāo)用戶">
<button id="send" type="button">發(fā)送</button>
</div>
</div>
</body>
</html>
其中js文件基本與前文的前面js文件內(nèi)容一致, 差異主要體現(xiàn)在三個(gè)地方:
- 連接成功后,訂閱的地址為“/user/queue/chat”, 該地址比服務(wù)端配置的地址多了“/user” 前綴,這是因?yàn)镾impMessagingTemplate類中自動(dòng)添加了路徑前綴。
- 聊天消息發(fā)送路徑為“/app/chat”。
- 發(fā)送的消息內(nèi)容中有一個(gè)to字段,該字段用來(lái)描述消息的目標(biāo)用戶。
3.2.6 測(cè)試
經(jīng)過(guò)如上幾個(gè)步驟之后,一個(gè)點(diǎn)對(duì)點(diǎn)的聊天服務(wù)就搭建成功了,接下來(lái)直接在瀏覽器地址欄中輸入http://calhost:8080/chat.html,首先會(huì)自動(dòng)跳轉(zhuǎn)到Spring Security的默認(rèn)登錄頁(yè)面,分別使用一開始配置的兩個(gè)用戶admin/123和suohe/123登錄,登錄成功后,就可以開始在線聊天了,如圖所示。文章來(lái)源:http://www.zghlxwxcb.cn/news/detail-452668.html
文章來(lái)源地址http://www.zghlxwxcb.cn/news/detail-452668.html
到了這里,關(guān)于Spring Boot整合WebSocket的文章就介紹完了。如果您還想了解更多內(nèi)容,請(qǐng)?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!