推薦WebSocket的三大理由:
- 1邪乍、采用全雙工通信降狠,擺脫傳統(tǒng)HTTP輪詢的窘境。
- 2庇楞、采用W3C國際標準榜配,完美支持HTML5。
- 3吕晌、簡單高效蛋褥,容易上手。
學習目標
快速學會通過WebSocket編寫簡單聊天功能睛驳。
快速查閱
專題閱讀:《SpringBoot 布道系列》
源碼下載:SpringBoot-WebSocket-Chat
溫馨提示:
1烙心、WebSocket是HTML5開始提供的一種在單個 TCP 連接上進行全雙工通訊的協(xié)議。在WebSocket API中乏沸,瀏覽器和服務(wù)器只需要做一個握手的動作淫茵,然后,瀏覽器和服務(wù)器之間就形成了一條快速通道蹬跃。兩者之間就直接可以數(shù)據(jù)互相傳送匙瘪。
2、瀏覽器通過 JavaScript 向服務(wù)器發(fā)出建立 WebSocket 連接的請求,連接建立以后丹喻,客戶端和服務(wù)器端就可以通過 TCP 連接直接交換數(shù)據(jù)薄货。
3、當你獲取 Web Socket 連接后驻啤,你可以通過 send() 方法來向服務(wù)器發(fā)送數(shù)據(jù)菲驴,并通過 onmessage 事件來接收服務(wù)器返回的數(shù)據(jù)。
使用教程
一骑冗、打造 WebSocket 聊天客戶端
溫馨提示:得益于W3C國際標準的實現(xiàn)赊瞬,我們在瀏覽器JS就能直接創(chuàng)建WebSocket對象,再通過簡單的回調(diào)函數(shù)就能完成WebSocket客戶端的編寫贼涩,非常簡單愚争!接下來讓我們一探究竟焦匈。
使用說明:
使用步驟:1禁灼、獲取WebSocket客戶端對象碉考。
例如: var webSocket = new WebSocket(url);
使用步驟:2、獲取WebSocket回調(diào)函數(shù)袒哥。
例如:webSocket.onmessage = function (event) {console.log('WebSocket收到消息:' + event.data);
事件類型 | WebSocket回調(diào)函數(shù) | 事件描述 |
---|---|---|
open | webSocket.onopen | 當打開連接后觸發(fā) |
message | webSocket.onmessage | 當客戶端接收服務(wù)端數(shù)據(jù)時觸發(fā) |
error | webSocket.onerror | 當通信異常時觸發(fā) |
close | webSocket.onclose | 當連接關(guān)閉時觸發(fā) |
使用步驟:3缩筛、發(fā)送消息給服務(wù)端
例如:webSokcet.send(jsonStr)
結(jié)合實際場景 本案例采用JSON字符串進行消息通信。
具體實現(xiàn):
下面是本案例在線聊天的客戶端實現(xiàn)的JS代碼堡称,附帶詳細注釋瞎抛。
<script>
/**
* WebSocket客戶端
*
* 使用說明:
* 1、WebSocket客戶端通過回調(diào)函數(shù)來接收服務(wù)端消息却紧。例如:webSocket.onmessage
* 2桐臊、WebSocket客戶端通過send方法來發(fā)送消息給服務(wù)端。例如:webSocket.send();
*/
function getWebSocket() {
/**
* WebSocket客戶端 PS:URL開頭表示W(wǎng)ebSocket協(xié)議 中間是域名端口 結(jié)尾是服務(wù)端映射地址
*/
var webSocket = new WebSocket('ws://localhost:8080/chat');
/**
* 當服務(wù)端打開連接
*/
webSocket.onopen = function (event) {
console.log('WebSocket打開連接');
};
/**
* 當服務(wù)端發(fā)來消息:1.廣播消息 2.更新在線人數(shù)
*/
webSocket.onmessage = function (event) {
console.log('WebSocket收到消息:%c' + event.data, 'color:green');
//獲取服務(wù)端消息
var message = JSON.parse(event.data) || {};
var $messageContainer = $('.message-container');
//喉嚨發(fā)炎
if (message.type === 'SPEAK') {
$messageContainer.append(
'<div class="mdui-card" style="margin: 10px 0;">' +
'<div class="mdui-card-primary">' +
'<div class="mdui-card-content message-content">' + message.username + ":" + message.msg + '</div>' +
'</div></div>');
}
$('.chat-num').text(message.onlineCount);
//防止刷屏
var $cards = $messageContainer.children('.mdui-card:visible').toArray();
if ($cards.length > 5) {
$cards.forEach(function (item, index) {
index < $cards.length - 5 && $(item).slideUp('fast');
});
}
};
/**
* 關(guān)閉連接
*/
webSocket.onclose = function (event) {
console.log('WebSocket關(guān)閉連接');
};
/**
* 通信失敗
*/
webSocket.onerror = function (event) {
console.log('WebSocket發(fā)生異常');
};
return webSocket;
}
var webSocket = getWebSocket();
/**
* 通過WebSocket對象發(fā)送消息給服務(wù)端
*/
function sendMsgToServer() {
var $message = $('#msg');
if ($message.val()) {
webSocket.send(JSON.stringify({username: $('#username').text(), msg: $message.val()}));
$message.val(null);
}
}
/**
* 清屏
*/
function clearMsg(){
$(".message-container").empty();
}
/**
* 使用ENTER發(fā)送消息
*/
document.onkeydown = function (event) {
var e = event || window.event || arguments.callee.caller.arguments[0];
e.keyCode === 13 && sendMsgToServer();
};
</script>
========================================================================
二晓殊、打造 WebSocket 聊天服務(wù)端
溫馨提示:得益于SpringBoot提供的自動配置断凶,我們只需要通過簡單注解
@ServerEndpoint
就就能創(chuàng)建WebSocket服務(wù)端,再通過簡單的回調(diào)函數(shù)就能完成WebSocket服務(wù)端的編寫巫俺,比起客戶端的使用同樣非常簡單认烁!
使用說明:
首先在POM文件引入spring-boot-starter-websocket
、thymeleaf 识藤、FastJson等依賴砚著。
使用步驟:1、開啟WebSocket服務(wù)端的自動注冊痴昧。
【這里需要特別提醒:ServerEndpointExporter 是由Spring官方提供的標準實現(xiàn),用于掃描ServerEndpointConfig配置類和@ServerEndpoint注解實例冠王。使用規(guī)則也很簡單:1.如果使用默認的嵌入式容器 比如Tomcat 則必須手工在上下文提供ServerEndpointExporter赶撰。2. 如果使用外部容器部署war包,則不要提供提供ServerEndpointExporter,因為此時SpringBoot默認將掃描服務(wù)端的行為交給外部容器處理豪娜。
】
@Configuration
public class WebSocketConfig {
@Bean
public ServerEndpointExporter serverEndpointExporter() {
return new ServerEndpointExporter();
}
}
使用步驟:2餐胀、創(chuàng)建WebSocket服務(wù)端。
核心思路:
- ① 通過注解
@ServerEndpoint
來聲明實例化WebSocket服務(wù)端瘤载。 - ② 通過注解
@OnOpen否灾、@OnMessage、@OnClose鸣奔、@OnError
來聲明回調(diào)函數(shù)墨技。
事件類型 | WebSocket服務(wù)端注解 | 事件描述 |
---|---|---|
open | @OnOpen | 當打開連接后觸發(fā) |
message | @OnMessage | 當接收客戶端信息時觸發(fā) |
error | @OnError | 當通信異常時觸發(fā) |
close | @OnClose | 當連接關(guān)閉時觸發(fā) |
- ③ 通過
ConcurrentHashMap
保存全部在線會話對象。
@Component
@ServerEndpoint("/chat")//標記此類為服務(wù)端
public class WebSocketChatServer {
/**
* 全部在線會話 PS: 基于場景考慮 這里使用線程安全的Map存儲會話對象挎狸。
*/
private static Map<String, Session> onlineSessions = new ConcurrentHashMap<>();
/**
* 當客戶端打開連接:1.添加會話對象 2.更新在線人數(shù)
*/
@OnOpen
public void onOpen(Session session) {
onlineSessions.put(session.getId(), session);
sendMessageToAll(Message.jsonStr(Message.ENTER, "", "", onlineSessions.size()));
}
/**
* 當客戶端發(fā)送消息:1.獲取它的用戶名和消息 2.發(fā)送消息給所有人
* <p>
* PS: 這里約定傳遞的消息為JSON字符串 方便傳遞更多參數(shù)扣汪!
*/
@OnMessage
public void onMessage(Session session, String jsonStr) {
Message message = JSON.parseObject(jsonStr, Message.class);
sendMessageToAll(Message.jsonStr(Message.SPEAK, message.getUsername(), message.getMsg(), onlineSessions.size()));
}
/**
* 當關(guān)閉連接:1.移除會話對象 2.更新在線人數(shù)
*/
@OnClose
public void onClose(Session session) {
onlineSessions.remove(session.getId());
sendMessageToAll(Message.jsonStr(Message.QUIT, "", "下線了!", onlineSessions.size()));
}
/**
* 當通信發(fā)生異常:打印錯誤日志
*/
@OnError
public void onError(Session session, Throwable error) {
error.printStackTrace();
}
/**
* 公共方法:發(fā)送信息給所有人
*/
private static void sendMessageToAll(String msg) {
onlineSessions.forEach((id, session) -> {
try {
session.getBasicRemote().sendText(msg);
} catch (IOException e) {
e.printStackTrace();
}
});
}
}
- ④ 通過會話對象
javax.websocket.Session
來發(fā)消息給客戶端锨匆。
/**
* WebSocket 聊天消息類
*/
package com.hehe.chat;
import com.alibaba.fastjson.JSON;
/**
* WebSocket 聊天消息類
*/
public class Message {
public static final String ENTER = "ENTER";
public static final String SPEAK = "SPEAK";
public static final String QUIT = "QUIT";
private String type;//消息類型
private String username; //發(fā)送人
private String msg; //發(fā)送消息
private int onlineCount; //在線用戶數(shù)
public static String jsonStr(String type, String username, String msg, int onlineTotal) {
return JSON.toJSONString(new Message(type, username, msg, onlineTotal));
}
public Message(String type, String username, String msg, int onlineCount) {
this.type = type;
this.username = username;
this.msg = msg;
this.onlineCount = onlineCount;
}
//這里省略get/set方法 請自行補充
}
三崭别、WebSocket在線聊天案例的視頻演示
1、源碼下載
至此恐锣,我們完成了客戶端和服務(wù)端的編碼茅主,由于篇幅有限,本教程的頁面代碼并未完整貼上土榴,想要完整的體驗效果請在Github下載源碼诀姚。傳送門:springboot-websocket-chat
2、視頻演示
上面一頓操作猛如虎鞭衩,實際到底是啥樣子呢学搜,接下來由哈士奇童鞋為我們演示最終版的在線聊天案例:
四、全文總結(jié)
1论衍、使用WebSocket用于實時雙向通訊的場景瑞佩,常見的如聊天室、跨系統(tǒng)消息推送等坯台。
2炬丸、創(chuàng)建WebSocket客戶端使用JS內(nèi)置對象+回調(diào)函數(shù)+send方法發(fā)送消息。
3蜒蕾、創(chuàng)建WebSocket服務(wù)端使用注解聲明實例+使用注解聲明回調(diào)方法+使用Session發(fā)送消息稠炬。