WebSocket 的使用
<p class="ds-markdown-paragraph">WebSocket 是一种在单个 TCP 连接上进行全双工通信的协议,允许服务器和客户端之间进行实时双向通信。</p><h2>基本使用</h2>
<h3>1. 创建 WebSocket 连接</h3>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 创建 WebSocket 连接</span>
const socket = <span style="color: rgba(0, 0, 255, 1)">new</span> WebSocket('ws://localhost:8080'<span style="color: rgba(0, 0, 0, 1)">);
</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 或者使用安全连接</span>
const secureSocket = <span style="color: rgba(0, 0, 255, 1)">new</span> WebSocket('wss://example.com/socket');</pre>
</div>
<h3>2. WebSocket 事件</h3>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 连接建立时触发</span>
socket.onopen = <span style="color: rgba(0, 0, 255, 1)">function</span><span style="color: rgba(0, 0, 0, 1)">(event) {
console.log(</span>'连接已建立'<span style="color: rgba(0, 0, 0, 1)">);
socket.send(</span>'Hello Server!'<span style="color: rgba(0, 0, 0, 1)">);
};
</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 接收到消息时触发</span>
socket.onmessage = <span style="color: rgba(0, 0, 255, 1)">function</span><span style="color: rgba(0, 0, 0, 1)">(event) {
console.log(</span>'收到消息:'<span style="color: rgba(0, 0, 0, 1)">, event.data);
</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 处理接收到的数据</span>
<span style="color: rgba(0, 0, 0, 1)">};
</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 发生错误时触发</span>
socket.onerror = <span style="color: rgba(0, 0, 255, 1)">function</span><span style="color: rgba(0, 0, 0, 1)">(error) {
console.error(</span>'WebSocket 错误:'<span style="color: rgba(0, 0, 0, 1)">, error);
};
</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 连接关闭时触发</span>
socket.onclose = <span style="color: rgba(0, 0, 255, 1)">function</span><span style="color: rgba(0, 0, 0, 1)">(event) {
console.log(</span>'连接关闭'<span style="color: rgba(0, 0, 0, 1)">, event.code, event.reason);
</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 可以在这里尝试重连</span>
};</pre>
</div>
<h2>完整示例</h2>
<h3>客户端示例</h3>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 0, 255, 1)"><!</span><span style="color: rgba(255, 0, 255, 1)">DOCTYPE html</span><span style="color: rgba(0, 0, 255, 1)">></span>
<span style="color: rgba(0, 0, 255, 1)"><</span><span style="color: rgba(128, 0, 0, 1)">html</span><span style="color: rgba(0, 0, 255, 1)">></span>
<span style="color: rgba(0, 0, 255, 1)"><</span><span style="color: rgba(128, 0, 0, 1)">head</span><span style="color: rgba(0, 0, 255, 1)">></span>
<span style="color: rgba(0, 0, 255, 1)"><</span><span style="color: rgba(128, 0, 0, 1)">title</span><span style="color: rgba(0, 0, 255, 1)">></span>WebSocket 示例<span style="color: rgba(0, 0, 255, 1)"></</span><span style="color: rgba(128, 0, 0, 1)">title</span><span style="color: rgba(0, 0, 255, 1)">></span>
<span style="color: rgba(0, 0, 255, 1)"></</span><span style="color: rgba(128, 0, 0, 1)">head</span><span style="color: rgba(0, 0, 255, 1)">></span>
<span style="color: rgba(0, 0, 255, 1)"><</span><span style="color: rgba(128, 0, 0, 1)">body</span><span style="color: rgba(0, 0, 255, 1)">></span>
<span style="color: rgba(0, 0, 255, 1)"><</span><span style="color: rgba(128, 0, 0, 1)">div</span><span style="color: rgba(0, 0, 255, 1)">></span>
<span style="color: rgba(0, 0, 255, 1)"><</span><span style="color: rgba(128, 0, 0, 1)">input </span><span style="color: rgba(255, 0, 0, 1)">type</span><span style="color: rgba(0, 0, 255, 1)">="text"</span><span style="color: rgba(255, 0, 0, 1)"> id</span><span style="color: rgba(0, 0, 255, 1)">="messageInput"</span><span style="color: rgba(255, 0, 0, 1)"> placeholder</span><span style="color: rgba(0, 0, 255, 1)">="输入消息"</span><span style="color: rgba(0, 0, 255, 1)">></span>
<span style="color: rgba(0, 0, 255, 1)"><</span><span style="color: rgba(128, 0, 0, 1)">button </span><span style="color: rgba(255, 0, 0, 1)">onclick</span><span style="color: rgba(0, 0, 255, 1)">="sendMessage()"</span><span style="color: rgba(0, 0, 255, 1)">></span>发送<span style="color: rgba(0, 0, 255, 1)"></</span><span style="color: rgba(128, 0, 0, 1)">button</span><span style="color: rgba(0, 0, 255, 1)">></span>
<span style="color: rgba(0, 0, 255, 1)"></</span><span style="color: rgba(128, 0, 0, 1)">div</span><span style="color: rgba(0, 0, 255, 1)">></span>
<span style="color: rgba(0, 0, 255, 1)"><</span><span style="color: rgba(128, 0, 0, 1)">div </span><span style="color: rgba(255, 0, 0, 1)">id</span><span style="color: rgba(0, 0, 255, 1)">="messages"</span><span style="color: rgba(0, 0, 255, 1)">></</span><span style="color: rgba(128, 0, 0, 1)">div</span><span style="color: rgba(0, 0, 255, 1)">></span>
<span style="color: rgba(0, 0, 255, 1)"><</span><span style="color: rgba(128, 0, 0, 1)">script</span><span style="color: rgba(0, 0, 255, 1)">></span>
<span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 128, 0, 1)">//</span><span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 128, 0, 1)"> 创建 WebSocket 连接</span>
<span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 0, 0, 1)"> const socket </span><span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 0, 0, 1)">=</span> <span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 0, 255, 1)">new</span><span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 0, 0, 1)"> WebSocket(</span><span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 0, 0, 1)">'</span><span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 0, 0, 1)">ws://localhost:8080</span><span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 0, 0, 1)">'</span><span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 0, 0, 1)">);
const messagesDiv </span><span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 0, 0, 1)">=</span><span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 0, 0, 1)"> document.getElementById(</span><span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 0, 0, 1)">'</span><span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 0, 0, 1)">messages</span><span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 0, 0, 1)">'</span><span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 0, 0, 1)">);
</span><span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 128, 0, 1)">//</span><span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 128, 0, 1)"> 连接建立</span>
<span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 0, 0, 1)"> socket.onopen </span><span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 0, 0, 1)">=</span> <span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 0, 255, 1)">function</span><span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 0, 0, 1)">() {
addMessage(</span><span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 0, 0, 1)">'</span><span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 0, 0, 1)">系统</span><span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 0, 0, 1)">'</span><span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 0, 0, 1)">, </span><span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 0, 0, 1)">'</span><span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 0, 0, 1)">连接成功!</span><span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 0, 0, 1)">'</span><span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 0, 0, 1)">);
};
</span><span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 128, 0, 1)">//</span><span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 128, 0, 1)"> 接收消息</span>
<span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 0, 0, 1)"> socket.onmessage </span><span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 0, 0, 1)">=</span> <span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 0, 255, 1)">function</span><span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 0, 0, 1)">(event) {
</span><span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 0, 255, 1)">try</span><span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 0, 0, 1)"> {
const data </span><span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 0, 0, 1)">=</span><span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 0, 0, 1)"> JSON.parse(event.data);
addMessage(data.sender, data.message);
} </span><span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 0, 255, 1)">catch</span><span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 0, 0, 1)"> (e) {
addMessage(</span><span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 0, 0, 1)">'</span><span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 0, 0, 1)">系统</span><span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 0, 0, 1)">'</span><span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 0, 0, 1)">, event.data);
}
};
</span><span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 128, 0, 1)">//</span><span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 128, 0, 1)"> 错误处理</span>
<span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 0, 0, 1)"> socket.onerror </span><span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 0, 0, 1)">=</span> <span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 0, 255, 1)">function</span><span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 0, 0, 1)">(error) {
addMessage(</span><span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 0, 0, 1)">'</span><span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 0, 0, 1)">系统</span><span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 0, 0, 1)">'</span><span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 0, 0, 1)">, </span><span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 0, 0, 1)">'</span><span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 0, 0, 1)">连接错误</span><span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 0, 0, 1)">'</span><span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 0, 0, 1)">);
};
</span><span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 128, 0, 1)">//</span><span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 128, 0, 1)"> 连接关闭</span>
<span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 0, 0, 1)"> socket.onclose </span><span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 0, 0, 1)">=</span> <span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 0, 255, 1)">function</span><span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 0, 0, 1)">() {
addMessage(</span><span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 0, 0, 1)">'</span><span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 0, 0, 1)">系统</span><span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 0, 0, 1)">'</span><span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 0, 0, 1)">, </span><span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 0, 0, 1)">'</span><span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 0, 0, 1)">连接已关闭</span><span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 0, 0, 1)">'</span><span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 0, 0, 1)">);
};
</span><span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 128, 0, 1)">//</span><span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 128, 0, 1)"> 发送消息</span>
<span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 0, 255, 1)">function</span><span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 0, 0, 1)"> sendMessage() {
const input </span><span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 0, 0, 1)">=</span><span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 0, 0, 1)"> document.getElementById(</span><span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 0, 0, 1)">'</span><span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 0, 0, 1)">messageInput</span><span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 0, 0, 1)">'</span><span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 0, 0, 1)">);
const message </span><span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 0, 0, 1)">=</span><span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 0, 0, 1)"> input.value.trim();
</span><span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 0, 255, 1)">if</span><span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 0, 0, 1)"> (message) {
socket.send(JSON.stringify({
type: </span><span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 0, 0, 1)">'</span><span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 0, 0, 1)">message</span><span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 0, 0, 1)">'</span><span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 0, 0, 1)">,
content: message,
timestamp: </span><span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 0, 255, 1)">new</span><span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 0, 0, 1)"> Date().toISOString()
}));
input.value </span><span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 0, 0, 1)">=</span> <span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 0, 0, 1)">''</span><span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 0, 0, 1)">;
}
}
</span><span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 128, 0, 1)">//</span><span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 128, 0, 1)"> 显示消息</span>
<span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 0, 255, 1)">function</span><span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 0, 0, 1)"> addMessage(sender, text) {
const msgElement </span><span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 0, 0, 1)">=</span><span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 0, 0, 1)"> document.createElement(</span><span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 0, 0, 1)">'</span><span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 0, 0, 1)">div</span><span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 0, 0, 1)">'</span><span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 0, 0, 1)">);
msgElement.innerHTML </span><span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 0, 0, 1)">=</span><span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 0, 0, 1)"> `</span><span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 0, 0, 1)"><</span><span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 0, 0, 1)">strong</span><span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 0, 0, 1)">></span><span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 0, 0, 1)">${sender}:</span><span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 0, 0, 1)"><</span><span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 0, 0, 1)">/</span><span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 0, 0, 1)">strong> ${text}`;</span>
<span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 0, 0, 1)"> messagesDiv.appendChild(msgElement);
messagesDiv.scrollTop </span><span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 0, 0, 1)">=</span><span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 0, 0, 1)"> messagesDiv.scrollHeight;
}
</span><span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 128, 0, 1)">//</span><span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 128, 0, 1)"> 关闭连接(页面卸载时)</span>
<span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 0, 0, 1)"> window.addEventListener(</span><span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 0, 0, 1)">'</span><span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 0, 0, 1)">beforeunload</span><span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 0, 0, 1)">'</span><span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 0, 0, 1)">, </span><span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 0, 255, 1)">function</span><span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 0, 0, 1)">() {
</span><span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 0, 255, 1)">if</span><span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 0, 0, 1)"> (socket.readyState </span><span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 0, 0, 1)">===</span><span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 0, 0, 1)"> WebSocket.OPEN) {
socket.close(</span><span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 0, 0, 1)">1000</span><span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 0, 0, 1)">, </span><span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 0, 0, 1)">'</span><span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 0, 0, 1)">用户离开页面</span><span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 0, 0, 1)">'</span><span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 0, 0, 1)">);
}
});
</span><span style="color: rgba(0, 0, 255, 1)"></</span><span style="color: rgba(128, 0, 0, 1)">script</span><span style="color: rgba(0, 0, 255, 1)">></span>
<span style="color: rgba(0, 0, 255, 1)"></</span><span style="color: rgba(128, 0, 0, 1)">body</span><span style="color: rgba(0, 0, 255, 1)">></span>
<span style="color: rgba(0, 0, 255, 1)"></</span><span style="color: rgba(128, 0, 0, 1)">html</span><span style="color: rgba(0, 0, 255, 1)">></span></pre>
</div>
<h3>Node.js 服务器端示例</h3>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 使用 ws 库</span>
const WebSocket = require('ws'<span style="color: rgba(0, 0, 0, 1)">);
</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 创建 WebSocket 服务器</span>
const wss = <span style="color: rgba(0, 0, 255, 1)">new</span> WebSocket.Server({ port: 8080<span style="color: rgba(0, 0, 0, 1)"> });
console.log(</span>'WebSocket 服务器启动在 ws://localhost:8080'<span style="color: rgba(0, 0, 0, 1)">);
</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 连接处理</span>
wss.on('connection', <span style="color: rgba(0, 0, 255, 1)">function</span><span style="color: rgba(0, 0, 0, 1)"> connection(ws) {
console.log(</span>'新客户端连接'<span style="color: rgba(0, 0, 0, 1)">);
</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 发送欢迎消息</span>
<span style="color: rgba(0, 0, 0, 1)"> ws.send(JSON.stringify({
type: </span>'system'<span style="color: rgba(0, 0, 0, 1)">,
message: </span>'欢迎连接到服务器!'<span style="color: rgba(0, 0, 0, 1)">
}));
</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 接收客户端消息</span>
ws.on('message', <span style="color: rgba(0, 0, 255, 1)">function</span><span style="color: rgba(0, 0, 0, 1)"> incoming(message) {
console.log(</span>'收到消息:'<span style="color: rgba(0, 0, 0, 1)">, message);
</span><span style="color: rgba(0, 0, 255, 1)">try</span><span style="color: rgba(0, 0, 0, 1)"> {
const data </span>=<span style="color: rgba(0, 0, 0, 1)"> JSON.parse(message);
</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 广播消息给所有客户端</span>
wss.clients.forEach(<span style="color: rgba(0, 0, 255, 1)">function</span><span style="color: rgba(0, 0, 0, 1)"> each(client) {
</span><span style="color: rgba(0, 0, 255, 1)">if</span> (client !== ws && client.readyState ===<span style="color: rgba(0, 0, 0, 1)"> WebSocket.OPEN) {
client.send(JSON.stringify({
type: </span>'message'<span style="color: rgba(0, 0, 0, 1)">,
sender: </span>'用户'<span style="color: rgba(0, 0, 0, 1)">,
message: data.content,
timestamp: </span><span style="color: rgba(0, 0, 255, 1)">new</span><span style="color: rgba(0, 0, 0, 1)"> Date().toISOString()
}));
}
});
} </span><span style="color: rgba(0, 0, 255, 1)">catch</span><span style="color: rgba(0, 0, 0, 1)"> (error) {
console.error(</span>'消息解析错误:'<span style="color: rgba(0, 0, 0, 1)">, error);
}
});
</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 连接关闭</span>
ws.on('close', <span style="color: rgba(0, 0, 255, 1)">function</span><span style="color: rgba(0, 0, 0, 1)">() {
console.log(</span>'客户端断开连接'<span style="color: rgba(0, 0, 0, 1)">);
});
</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 错误处理</span>
ws.on('error', <span style="color: rgba(0, 0, 255, 1)">function</span><span style="color: rgba(0, 0, 0, 1)">(error) {
console.error(</span>'WebSocket 错误:'<span style="color: rgba(0, 0, 0, 1)">, error);
});
});</span></pre>
</div>
<h2>WebSocket 状态</h2>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 检查连接状态</span>
<span style="color: rgba(0, 0, 255, 1)">switch</span><span style="color: rgba(0, 0, 0, 1)">(socket.readyState) {
</span><span style="color: rgba(0, 0, 255, 1)">case</span> WebSocket.CONNECTING:<span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 0 - 连接中</span>
console.log('连接中...'<span style="color: rgba(0, 0, 0, 1)">);
</span><span style="color: rgba(0, 0, 255, 1)">break</span><span style="color: rgba(0, 0, 0, 1)">;
</span><span style="color: rgba(0, 0, 255, 1)">case</span> WebSocket.OPEN: <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 1 - 已连接</span>
console.log('已连接'<span style="color: rgba(0, 0, 0, 1)">);
</span><span style="color: rgba(0, 0, 255, 1)">break</span><span style="color: rgba(0, 0, 0, 1)">;
</span><span style="color: rgba(0, 0, 255, 1)">case</span> WebSocket.CLOSING: <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 2 - 关闭中</span>
console.log('正在关闭...'<span style="color: rgba(0, 0, 0, 1)">);
</span><span style="color: rgba(0, 0, 255, 1)">break</span><span style="color: rgba(0, 0, 0, 1)">;
</span><span style="color: rgba(0, 0, 255, 1)">case</span> WebSocket.CLOSED: <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 3 - 已关闭</span>
console.log('已关闭'<span style="color: rgba(0, 0, 0, 1)">);
</span><span style="color: rgba(0, 0, 255, 1)">break</span><span style="color: rgba(0, 0, 0, 1)">;
}</span></pre>
</div>
<h2>高级特性</h2>
<h3>1. 心跳检测</h3>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 心跳检测</span>
<span style="color: rgba(0, 0, 0, 1)">let heartbeatInterval;
socket.onopen </span>= <span style="color: rgba(0, 0, 255, 1)">function</span><span style="color: rgba(0, 0, 0, 1)">() {
console.log(</span>'连接建立'<span style="color: rgba(0, 0, 0, 1)">);
</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 开始心跳</span>
heartbeatInterval = setInterval(() =><span style="color: rgba(0, 0, 0, 1)"> {
</span><span style="color: rgba(0, 0, 255, 1)">if</span> (socket.readyState ===<span style="color: rgba(0, 0, 0, 1)"> WebSocket.OPEN) {
socket.send(JSON.stringify({ type: </span>'ping'<span style="color: rgba(0, 0, 0, 1)"> }));
}
}, </span>30000<span style="color: rgba(0, 0, 0, 1)">);
};
socket.onclose </span>= <span style="color: rgba(0, 0, 255, 1)">function</span><span style="color: rgba(0, 0, 0, 1)">() {
</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 清除心跳</span>
<span style="color: rgba(0, 0, 0, 1)"> clearInterval(heartbeatInterval);
};</span></pre>
</div>
<h3>2. 重连机制</h3>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 0, 0, 1)">class WebSocketClient {
constructor(url) {
</span><span style="color: rgba(0, 0, 255, 1)">this</span>.url =<span style="color: rgba(0, 0, 0, 1)"> url;
</span><span style="color: rgba(0, 0, 255, 1)">this</span>.socket = <span style="color: rgba(0, 0, 255, 1)">null</span><span style="color: rgba(0, 0, 0, 1)">;
</span><span style="color: rgba(0, 0, 255, 1)">this</span>.reconnectAttempts = 0<span style="color: rgba(0, 0, 0, 1)">;
</span><span style="color: rgba(0, 0, 255, 1)">this</span>.maxReconnectAttempts = 5<span style="color: rgba(0, 0, 0, 1)">;
</span><span style="color: rgba(0, 0, 255, 1)">this</span>.reconnectDelay = 1000<span style="color: rgba(0, 0, 0, 1)">;
}
connect() {
</span><span style="color: rgba(0, 0, 255, 1)">this</span>.socket = <span style="color: rgba(0, 0, 255, 1)">new</span> WebSocket(<span style="color: rgba(0, 0, 255, 1)">this</span><span style="color: rgba(0, 0, 0, 1)">.url);
</span><span style="color: rgba(0, 0, 255, 1)">this</span>.socket.onopen = () =><span style="color: rgba(0, 0, 0, 1)"> {
console.log(</span>'连接成功'<span style="color: rgba(0, 0, 0, 1)">);
</span><span style="color: rgba(0, 0, 255, 1)">this</span>.reconnectAttempts = 0<span style="color: rgba(0, 0, 0, 1)">;
};
</span><span style="color: rgba(0, 0, 255, 1)">this</span>.socket.onclose = (event) =><span style="color: rgba(0, 0, 0, 1)"> {
console.log(</span>'连接断开,尝试重连...'<span style="color: rgba(0, 0, 0, 1)">);
</span><span style="color: rgba(0, 0, 255, 1)">this</span><span style="color: rgba(0, 0, 0, 1)">.reconnect();
};
</span><span style="color: rgba(0, 0, 255, 1)">this</span>.socket.onerror = (error) =><span style="color: rgba(0, 0, 0, 1)"> {
console.error(</span>'连接错误:'<span style="color: rgba(0, 0, 0, 1)">, error);
};
}
reconnect() {
</span><span style="color: rgba(0, 0, 255, 1)">if</span> (<span style="color: rgba(0, 0, 255, 1)">this</span>.reconnectAttempts < <span style="color: rgba(0, 0, 255, 1)">this</span><span style="color: rgba(0, 0, 0, 1)">.maxReconnectAttempts) {
</span><span style="color: rgba(0, 0, 255, 1)">this</span>.reconnectAttempts++<span style="color: rgba(0, 0, 0, 1)">;
const delay </span>= <span style="color: rgba(0, 0, 255, 1)">this</span>.reconnectDelay * Math.pow(2, <span style="color: rgba(0, 0, 255, 1)">this</span><span style="color: rgba(0, 0, 0, 1)">.reconnectAttempts);
setTimeout(() </span>=><span style="color: rgba(0, 0, 0, 1)"> {
console.log(`第 ${</span><span style="color: rgba(0, 0, 255, 1)">this</span><span style="color: rgba(0, 0, 0, 1)">.reconnectAttempts} 次重连`);
</span><span style="color: rgba(0, 0, 255, 1)">this</span><span style="color: rgba(0, 0, 0, 1)">.connect();
}, delay);
} </span><span style="color: rgba(0, 0, 255, 1)">else</span><span style="color: rgba(0, 0, 0, 1)"> {
console.error(</span>'重连次数已达上限'<span style="color: rgba(0, 0, 0, 1)">);
}
}
send(data) {
</span><span style="color: rgba(0, 0, 255, 1)">if</span> (<span style="color: rgba(0, 0, 255, 1)">this</span>.socket.readyState ===<span style="color: rgba(0, 0, 0, 1)"> WebSocket.OPEN) {
</span><span style="color: rgba(0, 0, 255, 1)">this</span><span style="color: rgba(0, 0, 0, 1)">.socket.send(data);
}
}
}</span></pre>
</div>
<h3>3. 二进制数据传输</h3>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 发送二进制数据</span>
socket.onopen = <span style="color: rgba(0, 0, 255, 1)">function</span><span style="color: rgba(0, 0, 0, 1)">() {
</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 发送 ArrayBuffer</span>
const buffer = <span style="color: rgba(0, 0, 255, 1)">new</span> ArrayBuffer(4<span style="color: rgba(0, 0, 0, 1)">);
const view </span>= <span style="color: rgba(0, 0, 255, 1)">new</span><span style="color: rgba(0, 0, 0, 1)"> Uint8Array(buffer);
view[</span>0] = 1<span style="color: rgba(0, 0, 0, 1)">;
view[</span>1] = 2<span style="color: rgba(0, 0, 0, 1)">;
view[</span>2] = 3<span style="color: rgba(0, 0, 0, 1)">;
view[</span>3] = 4<span style="color: rgba(0, 0, 0, 1)">;
socket.send(buffer);
</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 发送 Blob</span>
const blob = <span style="color: rgba(0, 0, 255, 1)">new</span> Blob(['Hello'], { type: 'text/plain'<span style="color: rgba(0, 0, 0, 1)"> });
socket.send(blob);
};
</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 接收二进制数据</span>
socket.binaryType = 'arraybuffer'; <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 或 'blob'</span>
<span style="color: rgba(0, 0, 0, 1)">
socket.onmessage </span>= <span style="color: rgba(0, 0, 255, 1)">function</span><span style="color: rgba(0, 0, 0, 1)">(event) {
</span><span style="color: rgba(0, 0, 255, 1)">if</span> (event.data <span style="color: rgba(0, 0, 255, 1)">instanceof</span><span style="color: rgba(0, 0, 0, 1)"> ArrayBuffer) {
</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 处理 ArrayBuffer</span>
const view = <span style="color: rgba(0, 0, 255, 1)">new</span><span style="color: rgba(0, 0, 0, 1)"> Uint8Array(event.data);
console.log(</span>'收到二进制数据:'<span style="color: rgba(0, 0, 0, 1)">, view);
} </span><span style="color: rgba(0, 0, 255, 1)">else</span><span style="color: rgba(0, 0, 0, 1)"> {
</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 处理文本数据</span>
console.log('收到文本数据:'<span style="color: rgba(0, 0, 0, 1)">, event.data);
}
};</span></pre>
</div>
<h2>Spring Boot 中使用 WebSocket</h2>
<h3>添加依赖</h3>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 0, 255, 1)"><</span><span style="color: rgba(128, 0, 0, 1)">dependency</span><span style="color: rgba(0, 0, 255, 1)">></span>
<span style="color: rgba(0, 0, 255, 1)"><</span><span style="color: rgba(128, 0, 0, 1)">groupId</span><span style="color: rgba(0, 0, 255, 1)">></span>org.springframework.boot<span style="color: rgba(0, 0, 255, 1)"></</span><span style="color: rgba(128, 0, 0, 1)">groupId</span><span style="color: rgba(0, 0, 255, 1)">></span>
<span style="color: rgba(0, 0, 255, 1)"><</span><span style="color: rgba(128, 0, 0, 1)">artifactId</span><span style="color: rgba(0, 0, 255, 1)">></span>spring-boot-starter-websocket<span style="color: rgba(0, 0, 255, 1)"></</span><span style="color: rgba(128, 0, 0, 1)">artifactId</span><span style="color: rgba(0, 0, 255, 1)">></span>
<span style="color: rgba(0, 0, 255, 1)"></</span><span style="color: rgba(128, 0, 0, 1)">dependency</span><span style="color: rgba(0, 0, 255, 1)">></span></pre>
</div>
<h3>基础配置类</h3>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 0, 0, 1)">@Configuration
@EnableWebSocketMessageBroker
</span><span style="color: rgba(0, 0, 255, 1)">public</span> <span style="color: rgba(0, 0, 255, 1)">class</span> WebSocketConfig <span style="color: rgba(0, 0, 255, 1)">implements</span><span style="color: rgba(0, 0, 0, 1)"> WebSocketMessageBrokerConfigurer {
@Override
</span><span style="color: rgba(0, 0, 255, 1)">public</span> <span style="color: rgba(0, 0, 255, 1)">void</span><span style="color: rgba(0, 0, 0, 1)"> configureMessageBroker(MessageBrokerRegistry config) {
</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 消息代理前缀</span>
config.enableSimpleBroker("/topic", "/queue"<span style="color: rgba(0, 0, 0, 1)">);
</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 应用目的地前缀</span>
config.setApplicationDestinationPrefixes("/app"<span style="color: rgba(0, 0, 0, 1)">);
</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 用户目的地前缀(一对一消息)</span>
config.setUserDestinationPrefix("/user"<span style="color: rgba(0, 0, 0, 1)">);
}
@Override
</span><span style="color: rgba(0, 0, 255, 1)">public</span> <span style="color: rgba(0, 0, 255, 1)">void</span><span style="color: rgba(0, 0, 0, 1)"> registerStompEndpoints(StompEndpointRegistry registry) {
</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 注册 WebSocket 端点</span>
registry.addEndpoint("/ws"<span style="color: rgba(0, 0, 0, 1)">)
.setAllowedOriginPatterns(</span>"*"<span style="color: rgba(0, 0, 0, 1)">)
.withSockJS();</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 支持 SockJS 降级
</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 也可以添加多个端点</span>
registry.addEndpoint("/ws-native"<span style="color: rgba(0, 0, 0, 1)">)
.setAllowedOriginPatterns(</span>"*"<span style="color: rgba(0, 0, 0, 1)">);
}
@Override
</span><span style="color: rgba(0, 0, 255, 1)">public</span> <span style="color: rgba(0, 0, 255, 1)">void</span><span style="color: rgba(0, 0, 0, 1)"> configureWebSocketTransport(WebSocketTransportRegistration registration) {
</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 配置传输限制</span>
registration.setMessageSizeLimit(128 * 1024); <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 消息大小限制 128KB</span>
registration.setSendTimeLimit(20 * 1000); <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 发送超时 20秒</span>
registration.setSendBufferSizeLimit(512 * 1024); <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 发送缓冲区限制 512KB</span>
<span style="color: rgba(0, 0, 0, 1)"> }
}</span></pre>
</div>
<h3>控制器示例</h3>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 0, 0, 1)">@Controller
</span><span style="color: rgba(0, 0, 255, 1)">public</span> <span style="color: rgba(0, 0, 255, 1)">class</span><span style="color: rgba(0, 0, 0, 1)"> WebSocketController {
</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 注入消息模板</span>
<span style="color: rgba(0, 0, 0, 1)"> @Autowired
</span><span style="color: rgba(0, 0, 255, 1)">private</span><span style="color: rgba(0, 0, 0, 1)"> SimpMessagingTemplate messagingTemplate;
</span><span style="color: rgba(0, 128, 0, 1)">/**</span><span style="color: rgba(0, 128, 0, 1)">
* 处理客户端发送的消息
* 目的地:/app/chat
</span><span style="color: rgba(0, 128, 0, 1)">*/</span><span style="color: rgba(0, 0, 0, 1)">
@MessageMapping(</span>"/chat"<span style="color: rgba(0, 0, 0, 1)">)
@SendTo(</span>"/topic/messages"<span style="color: rgba(0, 0, 0, 1)">)
</span><span style="color: rgba(0, 0, 255, 1)">public</span><span style="color: rgba(0, 0, 0, 1)"> ChatMessage handleMessage(ChatMessage message) {
message.setTimestamp(</span><span style="color: rgba(0, 0, 255, 1)">new</span><span style="color: rgba(0, 0, 0, 1)"> Date());
System.out.println(</span>"收到消息: " +<span style="color: rgba(0, 0, 0, 1)"> message.getContent());
</span><span style="color: rgba(0, 0, 255, 1)">return</span><span style="color: rgba(0, 0, 0, 1)"> message;
}
</span><span style="color: rgba(0, 128, 0, 1)">/**</span><span style="color: rgba(0, 128, 0, 1)">
* 发送广播消息
</span><span style="color: rgba(0, 128, 0, 1)">*/</span><span style="color: rgba(0, 0, 0, 1)">
@GetMapping(</span>"/broadcast"<span style="color: rgba(0, 0, 0, 1)">)
</span><span style="color: rgba(0, 0, 255, 1)">public</span> <span style="color: rgba(0, 0, 255, 1)">void</span><span style="color: rgba(0, 0, 0, 1)"> broadcast(String content) {
ChatMessage message </span>= <span style="color: rgba(0, 0, 255, 1)">new</span><span style="color: rgba(0, 0, 0, 1)"> ChatMessage();
message.setContent(content);
message.setSender(</span>"系统"<span style="color: rgba(0, 0, 0, 1)">);
message.setTimestamp(</span><span style="color: rgba(0, 0, 255, 1)">new</span><span style="color: rgba(0, 0, 0, 1)"> Date());
</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 发送到 /topic/messages</span>
messagingTemplate.convertAndSend("/topic/messages"<span style="color: rgba(0, 0, 0, 1)">, message);
}
</span><span style="color: rgba(0, 128, 0, 1)">/**</span><span style="color: rgba(0, 128, 0, 1)">
* 发送点对点消息
</span><span style="color: rgba(0, 128, 0, 1)">*/</span><span style="color: rgba(0, 0, 0, 1)">
@GetMapping(</span>"/sendToUser"<span style="color: rgba(0, 0, 0, 1)">)
</span><span style="color: rgba(0, 0, 255, 1)">public</span> <span style="color: rgba(0, 0, 255, 1)">void</span><span style="color: rgba(0, 0, 0, 1)"> sendToUser(String userId, String content) {
ChatMessage message </span>= <span style="color: rgba(0, 0, 255, 1)">new</span><span style="color: rgba(0, 0, 0, 1)"> ChatMessage();
message.setContent(content);
message.setSender(</span>"管理员"<span style="color: rgba(0, 0, 0, 1)">);
message.setTimestamp(</span><span style="color: rgba(0, 0, 255, 1)">new</span><span style="color: rgba(0, 0, 0, 1)"> Date());
</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 发送给指定用户:/user/{userId}/queue/messages</span>
<span style="color: rgba(0, 0, 0, 1)"> messagingTemplate.convertAndSendToUser(
userId,
</span>"/queue/messages"<span style="color: rgba(0, 0, 0, 1)">,
message
);
}
}
</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 消息实体类</span>
<span style="color: rgba(0, 0, 0, 1)">@Data
@AllArgsConstructor
@NoArgsConstructor
</span><span style="color: rgba(0, 0, 255, 1)">public</span> <span style="color: rgba(0, 0, 255, 1)">class</span><span style="color: rgba(0, 0, 0, 1)"> ChatMessage {
</span><span style="color: rgba(0, 0, 255, 1)">private</span><span style="color: rgba(0, 0, 0, 1)"> String sender;
</span><span style="color: rgba(0, 0, 255, 1)">private</span><span style="color: rgba(0, 0, 0, 1)"> String content;
</span><span style="color: rgba(0, 0, 255, 1)">private</span><span style="color: rgba(0, 0, 0, 1)"> Date timestamp;
}</span></pre>
</div>
<h3>连接拦截器</h3>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 0, 0, 1)">@Component
</span><span style="color: rgba(0, 0, 255, 1)">public</span> <span style="color: rgba(0, 0, 255, 1)">class</span> WebSocketInterceptor <span style="color: rgba(0, 0, 255, 1)">extends</span><span style="color: rgba(0, 0, 0, 1)"> ChannelInterceptorAdapter {
@Override
</span><span style="color: rgba(0, 0, 255, 1)">public</span> Message<?> preSend(Message<?><span style="color: rgba(0, 0, 0, 1)"> message, MessageChannel channel) {
StompHeaderAccessor accessor </span>=<span style="color: rgba(0, 0, 0, 1)">
MessageHeaderAccessor.getAccessor(message, StompHeaderAccessor.</span><span style="color: rgba(0, 0, 255, 1)">class</span><span style="color: rgba(0, 0, 0, 1)">);
</span><span style="color: rgba(0, 0, 255, 1)">if</span><span style="color: rgba(0, 0, 0, 1)"> (StompCommand.CONNECT.equals(accessor.getCommand())) {
</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 连接建立时处理</span>
String token = accessor.getFirstNativeHeader("token"<span style="color: rgba(0, 0, 0, 1)">);
</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 验证 token...</span>
System.out.println("用户连接: " +<span style="color: rgba(0, 0, 0, 1)"> accessor.getSessionId());
} </span><span style="color: rgba(0, 0, 255, 1)">else</span> <span style="color: rgba(0, 0, 255, 1)">if</span><span style="color: rgba(0, 0, 0, 1)"> (StompCommand.DISCONNECT.equals(accessor.getCommand())) {
</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 连接断开时处理</span>
System.out.println("用户断开: " +<span style="color: rgba(0, 0, 0, 1)"> accessor.getSessionId());
}
</span><span style="color: rgba(0, 0, 255, 1)">return</span><span style="color: rgba(0, 0, 0, 1)"> message;
}
}</span></pre>
</div>
<h2>原生 Java WebSocket(JSR 356)</h2>
<h3>注解方式</h3>
<div class="cnblogs_code">
<pre>@ServerEndpoint("/chat/{userId}"<span style="color: rgba(0, 0, 0, 1)">)
@Component
</span><span style="color: rgba(0, 0, 255, 1)">public</span> <span style="color: rgba(0, 0, 255, 1)">class</span><span style="color: rgba(0, 0, 0, 1)"> ChatEndpoint {
</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 存储所有连接</span>
<span style="color: rgba(0, 0, 255, 1)">private</span> <span style="color: rgba(0, 0, 255, 1)">static</span> <span style="color: rgba(0, 0, 255, 1)">final</span> Map<String, Session> sessions = <span style="color: rgba(0, 0, 255, 1)">new</span> ConcurrentHashMap<><span style="color: rgba(0, 0, 0, 1)">();
</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 存储用户ID和session的映射</span>
<span style="color: rgba(0, 0, 255, 1)">private</span> <span style="color: rgba(0, 0, 255, 1)">static</span> <span style="color: rgba(0, 0, 255, 1)">final</span> Map<String, String> userSessionMap = <span style="color: rgba(0, 0, 255, 1)">new</span> ConcurrentHashMap<><span style="color: rgba(0, 0, 0, 1)">();
</span><span style="color: rgba(0, 128, 0, 1)">/**</span><span style="color: rgba(0, 128, 0, 1)">
* 连接建立时调用
</span><span style="color: rgba(0, 128, 0, 1)">*/</span><span style="color: rgba(0, 0, 0, 1)">
@OnOpen
</span><span style="color: rgba(0, 0, 255, 1)">public</span> <span style="color: rgba(0, 0, 255, 1)">void</span> onOpen(Session session, @PathParam("userId"<span style="color: rgba(0, 0, 0, 1)">) String userId) {
System.out.println(</span>"连接建立: " + session.getId() + ", 用户: " +<span style="color: rgba(0, 0, 0, 1)"> userId);
</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 保存连接</span>
<span style="color: rgba(0, 0, 0, 1)"> sessions.put(session.getId(), session);
userSessionMap.put(userId, session.getId());
</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 通知其他用户有新用户上线</span>
broadcast("系统", "用户 " + userId + " 上线了"<span style="color: rgba(0, 0, 0, 1)">);
}
</span><span style="color: rgba(0, 128, 0, 1)">/**</span><span style="color: rgba(0, 128, 0, 1)">
* 收到消息时调用
</span><span style="color: rgba(0, 128, 0, 1)">*/</span><span style="color: rgba(0, 0, 0, 1)">
@OnMessage
</span><span style="color: rgba(0, 0, 255, 1)">public</span> <span style="color: rgba(0, 0, 255, 1)">void</span><span style="color: rgba(0, 0, 0, 1)"> onMessage(String message, Session session,
@PathParam(</span>"userId"<span style="color: rgba(0, 0, 0, 1)">) String userId) {
System.out.println(</span>"收到消息: " + message + " from: " +<span style="color: rgba(0, 0, 0, 1)"> userId);
</span><span style="color: rgba(0, 0, 255, 1)">try</span><span style="color: rgba(0, 0, 0, 1)"> {
</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 解析消息</span>
JSONObject json = <span style="color: rgba(0, 0, 255, 1)">new</span><span style="color: rgba(0, 0, 0, 1)"> JSONObject(message);
String content </span>= json.getString("content"<span style="color: rgba(0, 0, 0, 1)">);
String toUserId </span>= json.optString("to", <span style="color: rgba(0, 0, 255, 1)">null</span><span style="color: rgba(0, 0, 0, 1)">);
</span><span style="color: rgba(0, 0, 255, 1)">if</span> (toUserId != <span style="color: rgba(0, 0, 255, 1)">null</span><span style="color: rgba(0, 0, 0, 1)">) {
</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 私聊消息</span>
<span style="color: rgba(0, 0, 0, 1)"> sendToUser(userId, toUserId, content);
} </span><span style="color: rgba(0, 0, 255, 1)">else</span><span style="color: rgba(0, 0, 0, 1)"> {
</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 群发消息</span>
<span style="color: rgba(0, 0, 0, 1)"> broadcast(userId, content);
}
} </span><span style="color: rgba(0, 0, 255, 1)">catch</span><span style="color: rgba(0, 0, 0, 1)"> (Exception e) {
e.printStackTrace();
}
}
</span><span style="color: rgba(0, 128, 0, 1)">/**</span><span style="color: rgba(0, 128, 0, 1)">
* 连接关闭时调用
</span><span style="color: rgba(0, 128, 0, 1)">*/</span><span style="color: rgba(0, 0, 0, 1)">
@OnClose
</span><span style="color: rgba(0, 0, 255, 1)">public</span> <span style="color: rgba(0, 0, 255, 1)">void</span> onClose(Session session, @PathParam("userId"<span style="color: rgba(0, 0, 0, 1)">) String userId) {
System.out.println(</span>"连接关闭: " +<span style="color: rgba(0, 0, 0, 1)"> session.getId());
</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 移除连接</span>
<span style="color: rgba(0, 0, 0, 1)"> sessions.remove(session.getId());
userSessionMap.remove(userId);
</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 通知其他用户</span>
broadcast("系统", "用户 " + userId + " 下线了"<span style="color: rgba(0, 0, 0, 1)">);
}
</span><span style="color: rgba(0, 128, 0, 1)">/**</span><span style="color: rgba(0, 128, 0, 1)">
* 发生错误时调用
</span><span style="color: rgba(0, 128, 0, 1)">*/</span><span style="color: rgba(0, 0, 0, 1)">
@OnError
</span><span style="color: rgba(0, 0, 255, 1)">public</span> <span style="color: rgba(0, 0, 255, 1)">void</span><span style="color: rgba(0, 0, 0, 1)"> onError(Session session, Throwable error) {
System.out.println(</span>"连接错误: " +<span style="color: rgba(0, 0, 0, 1)"> session.getId());
error.printStackTrace();
}
</span><span style="color: rgba(0, 128, 0, 1)">/**</span><span style="color: rgba(0, 128, 0, 1)">
* 广播消息给所有用户
</span><span style="color: rgba(0, 128, 0, 1)">*/</span>
<span style="color: rgba(0, 0, 255, 1)">private</span> <span style="color: rgba(0, 0, 255, 1)">void</span><span style="color: rgba(0, 0, 0, 1)"> broadcast(String sender, String content) {
JSONObject message </span>= <span style="color: rgba(0, 0, 255, 1)">new</span><span style="color: rgba(0, 0, 0, 1)"> JSONObject();
message.put(</span>"sender"<span style="color: rgba(0, 0, 0, 1)">, sender);
message.put(</span>"content"<span style="color: rgba(0, 0, 0, 1)">, content);
message.put(</span>"timestamp"<span style="color: rgba(0, 0, 0, 1)">, System.currentTimeMillis());
message.put(</span>"type", "broadcast"<span style="color: rgba(0, 0, 0, 1)">);
</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 发送给所有连接的客户端</span>
<span style="color: rgba(0, 0, 255, 1)">for</span><span style="color: rgba(0, 0, 0, 1)"> (Session session : sessions.values()) {
</span><span style="color: rgba(0, 0, 255, 1)">if</span><span style="color: rgba(0, 0, 0, 1)"> (session.isOpen()) {
</span><span style="color: rgba(0, 0, 255, 1)">try</span><span style="color: rgba(0, 0, 0, 1)"> {
session.getAsyncRemote().sendText(message.toString());
} </span><span style="color: rgba(0, 0, 255, 1)">catch</span><span style="color: rgba(0, 0, 0, 1)"> (Exception e) {
e.printStackTrace();
}
}
}
}
</span><span style="color: rgba(0, 128, 0, 1)">/**</span><span style="color: rgba(0, 128, 0, 1)">
* 发送私聊消息
</span><span style="color: rgba(0, 128, 0, 1)">*/</span>
<span style="color: rgba(0, 0, 255, 1)">private</span> <span style="color: rgba(0, 0, 255, 1)">void</span><span style="color: rgba(0, 0, 0, 1)"> sendToUser(String fromUserId, String toUserId, String content) {
String toSessionId </span>=<span style="color: rgba(0, 0, 0, 1)"> userSessionMap.get(toUserId);
</span><span style="color: rgba(0, 0, 255, 1)">if</span> (toSessionId != <span style="color: rgba(0, 0, 255, 1)">null</span><span style="color: rgba(0, 0, 0, 1)">) {
Session toSession </span>=<span style="color: rgba(0, 0, 0, 1)"> sessions.get(toSessionId);
</span><span style="color: rgba(0, 0, 255, 1)">if</span> (toSession != <span style="color: rgba(0, 0, 255, 1)">null</span> &&<span style="color: rgba(0, 0, 0, 1)"> toSession.isOpen()) {
</span><span style="color: rgba(0, 0, 255, 1)">try</span><span style="color: rgba(0, 0, 0, 1)"> {
JSONObject message </span>= <span style="color: rgba(0, 0, 255, 1)">new</span><span style="color: rgba(0, 0, 0, 1)"> JSONObject();
message.put(</span>"sender"<span style="color: rgba(0, 0, 0, 1)">, fromUserId);
message.put(</span>"content"<span style="color: rgba(0, 0, 0, 1)">, content);
message.put(</span>"timestamp"<span style="color: rgba(0, 0, 0, 1)">, System.currentTimeMillis());
message.put(</span>"type", "private"<span style="color: rgba(0, 0, 0, 1)">);
toSession.getAsyncRemote().sendText(message.toString());
} </span><span style="color: rgba(0, 0, 255, 1)">catch</span><span style="color: rgba(0, 0, 0, 1)"> (Exception e) {
e.printStackTrace();
}
}
}
}
}</span></pre>
</div>
<h3>编程方式(继承 Endpoint 类)</h3>
<div class="cnblogs_code">
<pre>@ServerEndpoint("/game"<span style="color: rgba(0, 0, 0, 1)">)
</span><span style="color: rgba(0, 0, 255, 1)">public</span> <span style="color: rgba(0, 0, 255, 1)">class</span> GameEndpoint <span style="color: rgba(0, 0, 255, 1)">extends</span><span style="color: rgba(0, 0, 0, 1)"> Endpoint {
</span><span style="color: rgba(0, 0, 255, 1)">private</span> <span style="color: rgba(0, 0, 255, 1)">static</span> <span style="color: rgba(0, 0, 255, 1)">final</span> Set<Session> sessions = Collections.synchronizedSet(<span style="color: rgba(0, 0, 255, 1)">new</span> HashSet<><span style="color: rgba(0, 0, 0, 1)">());
@Override
</span><span style="color: rgba(0, 0, 255, 1)">public</span> <span style="color: rgba(0, 0, 255, 1)">void</span><span style="color: rgba(0, 0, 0, 1)"> onOpen(Session session, EndpointConfig config) {
System.out.println(</span>"新连接: " +<span style="color: rgba(0, 0, 0, 1)"> session.getId());
sessions.add(session);
</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 添加消息处理器</span>
session.addMessageHandler(<span style="color: rgba(0, 0, 255, 1)">new</span> MessageHandler.Whole<String><span style="color: rgba(0, 0, 0, 1)">() {
@Override
</span><span style="color: rgba(0, 0, 255, 1)">public</span> <span style="color: rgba(0, 0, 255, 1)">void</span><span style="color: rgba(0, 0, 0, 1)"> onMessage(String message) {
System.out.println(</span>"收到: " +<span style="color: rgba(0, 0, 0, 1)"> message);
</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 处理游戏逻辑</span>
<span style="color: rgba(0, 0, 0, 1)"> handleGameMessage(session, message);
}
});
</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 发送欢迎消息</span>
<span style="color: rgba(0, 0, 255, 1)">try</span><span style="color: rgba(0, 0, 0, 1)"> {
JSONObject welcome </span>= <span style="color: rgba(0, 0, 255, 1)">new</span><span style="color: rgba(0, 0, 0, 1)"> JSONObject();
welcome.put(</span>"type", "welcome"<span style="color: rgba(0, 0, 0, 1)">);
welcome.put(</span>"message", "欢迎加入游戏!"<span style="color: rgba(0, 0, 0, 1)">);
welcome.put(</span>"sessionId"<span style="color: rgba(0, 0, 0, 1)">, session.getId());
session.getBasicRemote().sendText(welcome.toString());
} </span><span style="color: rgba(0, 0, 255, 1)">catch</span><span style="color: rgba(0, 0, 0, 1)"> (IOException e) {
e.printStackTrace();
}
}
@Override
</span><span style="color: rgba(0, 0, 255, 1)">public</span> <span style="color: rgba(0, 0, 255, 1)">void</span><span style="color: rgba(0, 0, 0, 1)"> onClose(Session session, CloseReason closeReason) {
System.out.println(</span>"连接关闭: " +<span style="color: rgba(0, 0, 0, 1)"> session.getId());
sessions.remove(session);
</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 通知其他玩家</span>
<span style="color: rgba(0, 0, 0, 1)"> broadcastPlayerLeft(session.getId());
}
@Override
</span><span style="color: rgba(0, 0, 255, 1)">public</span> <span style="color: rgba(0, 0, 255, 1)">void</span><span style="color: rgba(0, 0, 0, 1)"> onError(Session session, Throwable thr) {
System.err.println(</span>"连接错误: " +<span style="color: rgba(0, 0, 0, 1)"> session.getId());
thr.printStackTrace();
}
</span><span style="color: rgba(0, 0, 255, 1)">private</span> <span style="color: rgba(0, 0, 255, 1)">void</span><span style="color: rgba(0, 0, 0, 1)"> handleGameMessage(Session session, String message) {
</span><span style="color: rgba(0, 0, 255, 1)">try</span><span style="color: rgba(0, 0, 0, 1)"> {
JSONObject json </span>= <span style="color: rgba(0, 0, 255, 1)">new</span><span style="color: rgba(0, 0, 0, 1)"> JSONObject(message);
String type </span>= json.getString("type"<span style="color: rgba(0, 0, 0, 1)">);
</span><span style="color: rgba(0, 0, 255, 1)">switch</span><span style="color: rgba(0, 0, 0, 1)"> (type) {
</span><span style="color: rgba(0, 0, 255, 1)">case</span> "move"<span style="color: rgba(0, 0, 0, 1)">:
</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 处理移动</span>
<span style="color: rgba(0, 0, 0, 1)"> handlePlayerMove(session, json);
</span><span style="color: rgba(0, 0, 255, 1)">break</span><span style="color: rgba(0, 0, 0, 1)">;
</span><span style="color: rgba(0, 0, 255, 1)">case</span> "chat"<span style="color: rgba(0, 0, 0, 1)">:
</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 处理聊天</span>
<span style="color: rgba(0, 0, 0, 1)"> handleChatMessage(session, json);
</span><span style="color: rgba(0, 0, 255, 1)">break</span><span style="color: rgba(0, 0, 0, 1)">;
</span><span style="color: rgba(0, 0, 255, 1)">default</span><span style="color: rgba(0, 0, 0, 1)">:
System.out.println(</span>"未知消息类型: " +<span style="color: rgba(0, 0, 0, 1)"> type);
}
} </span><span style="color: rgba(0, 0, 255, 1)">catch</span><span style="color: rgba(0, 0, 0, 1)"> (Exception e) {
e.printStackTrace();
}
}
</span><span style="color: rgba(0, 0, 255, 1)">private</span> <span style="color: rgba(0, 0, 255, 1)">void</span><span style="color: rgba(0, 0, 0, 1)"> handlePlayerMove(Session session, JSONObject moveData) {
</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 处理玩家移动逻辑
</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 广播给所有玩家</span>
<span style="color: rgba(0, 0, 0, 1)"> broadcastGameUpdate(moveData);
}
</span><span style="color: rgba(0, 0, 255, 1)">private</span> <span style="color: rgba(0, 0, 255, 1)">void</span><span style="color: rgba(0, 0, 0, 1)"> handleChatMessage(Session session, JSONObject chatData) {
</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 广播聊天消息</span>
JSONObject broadcastMsg = <span style="color: rgba(0, 0, 255, 1)">new</span><span style="color: rgba(0, 0, 0, 1)"> JSONObject();
broadcastMsg.put(</span>"type", "chat"<span style="color: rgba(0, 0, 0, 1)">);
broadcastMsg.put(</span>"sender"<span style="color: rgba(0, 0, 0, 1)">, session.getId());
broadcastMsg.put(</span>"message", chatData.getString("message"<span style="color: rgba(0, 0, 0, 1)">));
broadcastMsg.put(</span>"timestamp"<span style="color: rgba(0, 0, 0, 1)">, System.currentTimeMillis());
broadcast(broadcastMsg.toString());
}
</span><span style="color: rgba(0, 0, 255, 1)">private</span> <span style="color: rgba(0, 0, 255, 1)">void</span><span style="color: rgba(0, 0, 0, 1)"> broadcast(String message) {
</span><span style="color: rgba(0, 0, 255, 1)">synchronized</span><span style="color: rgba(0, 0, 0, 1)"> (sessions) {
</span><span style="color: rgba(0, 0, 255, 1)">for</span><span style="color: rgba(0, 0, 0, 1)"> (Session s : sessions) {
</span><span style="color: rgba(0, 0, 255, 1)">if</span><span style="color: rgba(0, 0, 0, 1)"> (s.isOpen()) {
</span><span style="color: rgba(0, 0, 255, 1)">try</span><span style="color: rgba(0, 0, 0, 1)"> {
s.getBasicRemote().sendText(message);
} </span><span style="color: rgba(0, 0, 255, 1)">catch</span><span style="color: rgba(0, 0, 0, 1)"> (IOException e) {
e.printStackTrace();
}
}
}
}
}
}</span></pre>
</div>
<h2> 配置文件</h2>
<h3>application.yml 配置</h3>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 0, 0, 1)">spring:
websocket:
# WebSocket 配置
enabled: true
server:
# 服务器配置
port: 8080
servlet:
context-path: /api
# 自定义配置
websocket:
max-sessions: 1000
heartbeat-interval: 30000
max-message-size: 128KB</span></pre>
</div>
<h2>心跳检测和连接管理</h2>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 0, 0, 1)">@Component
</span><span style="color: rgba(0, 0, 255, 1)">public</span> <span style="color: rgba(0, 0, 255, 1)">class</span><span style="color: rgba(0, 0, 0, 1)"> WebSocketHeartbeat {
@Autowired
</span><span style="color: rgba(0, 0, 255, 1)">private</span><span style="color: rgba(0, 0, 0, 1)"> SimpMessagingTemplate messagingTemplate;
</span><span style="color: rgba(0, 0, 255, 1)">private</span> ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(1<span style="color: rgba(0, 0, 0, 1)">);
@PostConstruct
</span><span style="color: rgba(0, 0, 255, 1)">public</span> <span style="color: rgba(0, 0, 255, 1)">void</span><span style="color: rgba(0, 0, 0, 1)"> init() {
</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 每30秒发送一次心跳</span>
scheduler.scheduleAtFixedRate(() -><span style="color: rgba(0, 0, 0, 1)"> {
</span><span style="color: rgba(0, 0, 255, 1)">try</span><span style="color: rgba(0, 0, 0, 1)"> {
messagingTemplate.convertAndSend(</span>"/topic/heartbeat"<span style="color: rgba(0, 0, 0, 1)">,
Map.of(</span>"timestamp"<span style="color: rgba(0, 0, 0, 1)">, System.currentTimeMillis(),
</span>"type", "heartbeat"<span style="color: rgba(0, 0, 0, 1)">));
} </span><span style="color: rgba(0, 0, 255, 1)">catch</span><span style="color: rgba(0, 0, 0, 1)"> (Exception e) {
e.printStackTrace();
}
}, </span>0, 30<span style="color: rgba(0, 0, 0, 1)">, TimeUnit.SECONDS);
}
@PreDestroy
</span><span style="color: rgba(0, 0, 255, 1)">public</span> <span style="color: rgba(0, 0, 255, 1)">void</span><span style="color: rgba(0, 0, 0, 1)"> destroy() {
scheduler.shutdown();
}
}</span></pre>
</div>
<h2>消息编码器/解码器</h2>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 自定义消息编解码器</span>
<span style="color: rgba(0, 0, 0, 1)">@Component
</span><span style="color: rgba(0, 0, 255, 1)">public</span> <span style="color: rgba(0, 0, 255, 1)">class</span> ChatMessageConverter <span style="color: rgba(0, 0, 255, 1)">implements</span><span style="color: rgba(0, 0, 0, 1)"> MessageConverter {
@Override
</span><span style="color: rgba(0, 0, 255, 1)">public</span> Message<?><span style="color: rgba(0, 0, 0, 1)"> toMessage(Object payload, MessageHeaders headers) {
</span><span style="color: rgba(0, 0, 255, 1)">if</span> (payload <span style="color: rgba(0, 0, 255, 1)">instanceof</span><span style="color: rgba(0, 0, 0, 1)"> ChatMessage) {
ChatMessage msg </span>=<span style="color: rgba(0, 0, 0, 1)"> (ChatMessage) payload;
</span><span style="color: rgba(0, 0, 255, 1)">byte</span>[] bytes =<span style="color: rgba(0, 0, 0, 1)"> serializeMessage(msg);
</span><span style="color: rgba(0, 0, 255, 1)">return</span><span style="color: rgba(0, 0, 0, 1)"> MessageBuilder.createMessage(bytes, headers);
}
</span><span style="color: rgba(0, 0, 255, 1)">return</span> <span style="color: rgba(0, 0, 255, 1)">null</span><span style="color: rgba(0, 0, 0, 1)">;
}
@Override
</span><span style="color: rgba(0, 0, 255, 1)">public</span> Object fromMessage(Message<?> message, Class<?><span style="color: rgba(0, 0, 0, 1)"> targetClass) {
</span><span style="color: rgba(0, 0, 255, 1)">if</span> (targetClass == ChatMessage.<span style="color: rgba(0, 0, 255, 1)">class</span><span style="color: rgba(0, 0, 0, 1)">) {
</span><span style="color: rgba(0, 0, 255, 1)">byte</span>[] bytes = (<span style="color: rgba(0, 0, 255, 1)">byte</span><span style="color: rgba(0, 0, 0, 1)">[]) message.getPayload();
</span><span style="color: rgba(0, 0, 255, 1)">return</span><span style="color: rgba(0, 0, 0, 1)"> deserializeMessage(bytes);
}
</span><span style="color: rgba(0, 0, 255, 1)">return</span> <span style="color: rgba(0, 0, 255, 1)">null</span><span style="color: rgba(0, 0, 0, 1)">;
}
</span><span style="color: rgba(0, 0, 255, 1)">private</span> <span style="color: rgba(0, 0, 255, 1)">byte</span><span style="color: rgba(0, 0, 0, 1)">[] serializeMessage(ChatMessage message) {
</span><span style="color: rgba(0, 0, 255, 1)">try</span><span style="color: rgba(0, 0, 0, 1)"> {
</span><span style="color: rgba(0, 0, 255, 1)">return</span> <span style="color: rgba(0, 0, 255, 1)">new</span><span style="color: rgba(0, 0, 0, 1)"> ObjectMapper().writeValueAsBytes(message);
} </span><span style="color: rgba(0, 0, 255, 1)">catch</span><span style="color: rgba(0, 0, 0, 1)"> (Exception e) {
</span><span style="color: rgba(0, 0, 255, 1)">throw</span> <span style="color: rgba(0, 0, 255, 1)">new</span> RuntimeException("序列化失败"<span style="color: rgba(0, 0, 0, 1)">, e);
}
}
</span><span style="color: rgba(0, 0, 255, 1)">private</span> ChatMessage deserializeMessage(<span style="color: rgba(0, 0, 255, 1)">byte</span><span style="color: rgba(0, 0, 0, 1)">[] bytes) {
</span><span style="color: rgba(0, 0, 255, 1)">try</span><span style="color: rgba(0, 0, 0, 1)"> {
</span><span style="color: rgba(0, 0, 255, 1)">return</span> <span style="color: rgba(0, 0, 255, 1)">new</span> ObjectMapper().readValue(bytes, ChatMessage.<span style="color: rgba(0, 0, 255, 1)">class</span><span style="color: rgba(0, 0, 0, 1)">);
} </span><span style="color: rgba(0, 0, 255, 1)">catch</span><span style="color: rgba(0, 0, 0, 1)"> (Exception e) {
</span><span style="color: rgba(0, 0, 255, 1)">throw</span> <span style="color: rgba(0, 0, 255, 1)">new</span> RuntimeException("反序列化失败"<span style="color: rgba(0, 0, 0, 1)">, e);
}
}
}</span></pre>
</div>
<h2>集群支持</h2>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 0, 0, 1)">@Configuration
@EnableRedisRepositories
</span><span style="color: rgba(0, 0, 255, 1)">public</span> <span style="color: rgba(0, 0, 255, 1)">class</span><span style="color: rgba(0, 0, 0, 1)"> RedisConfig {
@Bean
</span><span style="color: rgba(0, 0, 255, 1)">public</span><span style="color: rgba(0, 0, 0, 1)"> RedisMessageListenerContainer redisContainer(RedisConnectionFactory connectionFactory) {
RedisMessageListenerContainer container </span>= <span style="color: rgba(0, 0, 255, 1)">new</span><span style="color: rgba(0, 0, 0, 1)"> RedisMessageListenerContainer();
container.setConnectionFactory(connectionFactory);
</span><span style="color: rgba(0, 0, 255, 1)">return</span><span style="color: rgba(0, 0, 0, 1)"> container;
}
@Bean
</span><span style="color: rgba(0, 0, 255, 1)">public</span> RedisTemplate<String, Object><span style="color: rgba(0, 0, 0, 1)"> redisTemplate(RedisConnectionFactory factory) {
RedisTemplate</span><String, Object> template = <span style="color: rgba(0, 0, 255, 1)">new</span> RedisTemplate<><span style="color: rgba(0, 0, 0, 1)">();
template.setConnectionFactory(factory);
template.setKeySerializer(</span><span style="color: rgba(0, 0, 255, 1)">new</span><span style="color: rgba(0, 0, 0, 1)"> StringRedisSerializer());
template.setValueSerializer(</span><span style="color: rgba(0, 0, 255, 1)">new</span> Jackson2JsonRedisSerializer<>(Object.<span style="color: rgba(0, 0, 255, 1)">class</span><span style="color: rgba(0, 0, 0, 1)">));
</span><span style="color: rgba(0, 0, 255, 1)">return</span><span style="color: rgba(0, 0, 0, 1)"> template;
}
}
</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> Redis 广播消息</span>
<span style="color: rgba(0, 0, 0, 1)">@Component
</span><span style="color: rgba(0, 0, 255, 1)">public</span> <span style="color: rgba(0, 0, 255, 1)">class</span><span style="color: rgba(0, 0, 0, 1)"> RedisMessagePublisher {
@Autowired
</span><span style="color: rgba(0, 0, 255, 1)">private</span> RedisTemplate<String, Object><span style="color: rgba(0, 0, 0, 1)"> redisTemplate;
</span><span style="color: rgba(0, 0, 255, 1)">public</span> <span style="color: rgba(0, 0, 255, 1)">void</span><span style="color: rgba(0, 0, 0, 1)"> publish(String channel, Object message) {
redisTemplate.convertAndSend(channel, message);
}
}
@Component
</span><span style="color: rgba(0, 0, 255, 1)">public</span> <span style="color: rgba(0, 0, 255, 1)">class</span> RedisMessageSubscriber <span style="color: rgba(0, 0, 255, 1)">implements</span><span style="color: rgba(0, 0, 0, 1)"> MessageListener {
@Autowired
</span><span style="color: rgba(0, 0, 255, 1)">private</span><span style="color: rgba(0, 0, 0, 1)"> SimpMessagingTemplate messagingTemplate;
@Override
</span><span style="color: rgba(0, 0, 255, 1)">public</span> <span style="color: rgba(0, 0, 255, 1)">void</span> onMessage(Message message, <span style="color: rgba(0, 0, 255, 1)">byte</span><span style="color: rgba(0, 0, 0, 1)">[] pattern) {
</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 处理从 Redis 收到的消息
</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 转发给 WebSocket 客户端</span>
String channel = <span style="color: rgba(0, 0, 255, 1)">new</span><span style="color: rgba(0, 0, 0, 1)"> String(pattern);
String msg </span>= <span style="color: rgba(0, 0, 255, 1)">new</span><span style="color: rgba(0, 0, 0, 1)"> String(message.getBody());
messagingTemplate.convertAndSend(</span>"/topic/" +<span style="color: rgba(0, 0, 0, 1)"> channel, msg);
}
}</span></pre>
</div>
<p>Spring Boot 的 STOMP 实现更加完整和易于使用,而原生 WebSocket 则更加灵活。</p><br><br>
来源:https://www.cnblogs.com/syf0824/p/19368779
頁:
[1]