概述
WebSocket的故事系列計劃分五大篇六章,旨在由淺入深的介紹WebSocket以及在Springboot中如何快速構建和使用WebSocket提供的能力。本系列計劃包含如下幾篇文章:
第一篇,什麼是WebSocket以及它的用途
第二篇,Spring中如何利用STOMP快速構建WebSocket廣播式訊息模式
第三篇,Springboot中,如何利用WebSocket和STOMP快速構建點對點的訊息模式(1)
第四篇,Springboot中,如何利用WebSocket和STOMP快速構建點對點的訊息模式(2)
第五篇,Springboot中,實現網頁聊天室之自定義WebSocket訊息代理
第六篇,Springboot中,實現更靈活的WebSocket
本篇的主線
本篇是這個系列的最後一篇,將介紹另一種實現WebSocket的方式。仍然會以一個簡單聊天室為例子進行講述。至此我們也可以根據具體情況,選擇不同的實現方式。
本篇適合的讀者
想了解如何在Springboot上自定義實現更為複雜的WebSocket產品邏輯的各路有志青年。
使用Tomcat提供的WebSocket支援
早在Java EE 7
時,就釋出了JSR356
規範。Tomcat7.0.47開始,也支援了統一的WebSocket
介面。在使用Springboot
時,也可以輕鬆的使用Tomcat
提供的這些API。今天我們就來體驗一把Tomcat
實現的WebSocket
。
1. 引入依賴
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-websocket</artifactId>
</dependency>
複製程式碼
Springboot內建了tomcat,我們直接引入spring的這個高階元件即可。順便多說一句,Springboot的高階元件會自動引用基礎的元件,像spring-boot-starter-websocket
就引入了spring-boot-starter-web
和spring-boot-starter
,所以不要重複引入。
2. 使用@ServerEndpoint建立WebSocket Endpoint
首先要注入ServerEndpointExporter
,這個Bean會自動註冊使用了@ServerEndpoint
註解宣告的WebSocket Endpoint。
package com.draw.wsdraw.websocket;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.socket.server.standard.ServerEndpointExporter;
@Configuration
public class WebSocketConfig {
@Bean
public ServerEndpointExporter serverEndpointExporter() {
return new ServerEndpointExporter();
}
}
複製程式碼
然後,我們動手實現WebSocket服務的實現類,這裡是WebSocketServer
,注意別忘了用@ServerEndpint
和@Component
宣告下。雖然@Component
預設是單例模式的,但Springboot還是會為每個WebSocket連線初始化一個Bean
,所以可以用一個靜態Map儲存起來。換句話說,每當有一個使用者向伺服器發起連線時,都會建立一個WebSocketServer
物件,將此物件按roomId
儲存在HashMap
中,方便後續使用。
建立ServerEndpoint時,需要對應實現其所需的幾個功能性方法:OnOpen、OnMessage、OnClose、OnError
。
@OnOpen
:客戶端向服務端發起建立連線時,服務端呼叫,可傳入的引數為Session(WebSocket的Session)和EndpointConfig。另外,還可以加入帶@PathParam註解的引數。這裡我們註解的引數是roomId,即在建立連線時,攜帶的請求地址上的引數,與我們上一篇中介紹的{INFO}是一樣的作用。@OnMessage
:客戶端訊息到來時呼叫,包含會話Session,根據訊息的形式,如果是文字訊息,傳入String型別引數或者Reader,如果是二進位制訊息,傳入byte[]型別引數或者InputStream。@OnClose
:當斷開連線,關閉WebSocket時呼叫。@OnError
:當發生錯誤時呼叫,傳入異常Session和錯誤資訊。
重寫上述方法,即可實現WebSocket的服務端業務邏輯。
@ServerEndpoint("/webSocket/{roomId}")
@Component
public class WebSocketServer {
private static ConcurrentHashMap<String, List<WebSocketServer>> webSocketMap =
new ConcurrentHashMap<>(3);
//與某個客戶端的連線會話,需要通過它來給客戶端傳送資料
private Session session;
//接收roomId
private String roomId = "";
/**
* 連線建立成功呼叫的方法
*/
@OnOpen
public void onOpen(Session session, EndpointConfig config, @PathParam("roomId") String roomId) {
if (roomId == null || roomId.isEmpty()) return;
this.session = session;
this.roomId = roomId;
addSocketServer2Map(this);
try {
sendMessage("連線成功", true);
} catch (IOException e) {
}
}
/**
* 連線關閉呼叫的方法
*/
@OnClose
public void onClose() {
List<WebSocketServer> wssList = webSocketMap.get(roomId);
if (wssList != null) {
for (WebSocketServer item : wssList) {
if (item.session.getId().equals(session.getId())) {
wssList.remove(item);
if (wssList.isEmpty()) {
webSocketMap.remove(roomId);
}
break;
}
}
}
}
/**
* 收到客戶端訊息後呼叫的方法
*/
@OnMessage
public void onMessage(String message, Session session) {
//群發訊息
String msg = filterMessage(message);
if (msg != null) {
sendInfo(msg, roomId, session);
}
}
/**
* 發生錯誤時,呼叫的方法
*/
@OnError
public void onError(Session session, Throwable error) {
}
複製程式碼
這樣,服務端的程式碼就實現完了,這裡僅貼出來部分原始碼,文後會給出專案原始碼地址。
3. 實現客戶端頁面
<script type="text/javascript">
var ws;
function setConnected(connected){
document.getElementById('connect').disabled = connected;
document.getElementById('disconnect').disabled = !connected;
document.getElementById('conversationDiv').style.visibility = connected ? 'visible' : 'hidden';
$("#response").html();
}
function connect(){
var roomId = $('#roomId').val();
ws = new WebSocket('ws://localhost:8080/webSocket/' + roomId);
ws.onopen = WSonOpen;
ws.onmessage = WSonMessage;
ws.onclose = WSonClose;
ws.onerror = WSonError;
}
function WSonOpen() {
var message = {
name:'Server',
chatContent:'成功連線'
}
setConnected(true);
showResponse(message)
};
function WSonMessage(event) {
var message = {
name:'Server',
chatContent:event.data
}
showResponse(message)
};
function WSonClose() {
var message = {
name:'Server',
chatContent:'已斷開'
}
showResponse(message)
};
function WSonError() {
var message = {
name:'Server',
chatContent:'連線錯誤!'
}
showResponse(message)
};
function disconnect(){
ws.close()
setConnected(false);
console.log("Disconnected");
}
function sendMessage(){
var chatContent = $("#chatContent").val();
var roomId = $('#roomId').val();
ws.send(JSON.stringify({'roomId':roomId,'chatContent':chatContent}))
}
function showResponse(message){
var response = $("#response").val();
$("#response").val(response+message.name+': '+message.chatContent+'\n');
}
</script>
複製程式碼
客戶端頁面實現了簡單的連線、斷開和訊息傳送功能。這部分就不詳細介紹了。
4. 演示截圖
原始碼地址
本篇的原始碼地址:
總結
本篇直接使用了Tomcat提供的WebSocket,也是一種相對靈活的實現方式,只需要按照上述步驟來實現即可。集中精力編寫業務邏輯程式碼。
整個一個系列下來,我們介紹了幾種實現WebSocket的方式,有的整合度高,有些相對靈活。大家可以按實際業務需求來選取合適的方式。至此,這個系列就結束了。感謝大家閱讀。
小銘出品,必屬精品
歡迎關注xNPE技術論壇,更多原創乾貨每日推送。