不会取昵称 發表於 2025-12-18 20:27:00

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)">&lt;!</span><span style="color: rgba(255, 0, 255, 1)">DOCTYPE html</span><span style="color: rgba(0, 0, 255, 1)">&gt;</span>
<span style="color: rgba(0, 0, 255, 1)">&lt;</span><span style="color: rgba(128, 0, 0, 1)">html</span><span style="color: rgba(0, 0, 255, 1)">&gt;</span>
<span style="color: rgba(0, 0, 255, 1)">&lt;</span><span style="color: rgba(128, 0, 0, 1)">head</span><span style="color: rgba(0, 0, 255, 1)">&gt;</span>
    <span style="color: rgba(0, 0, 255, 1)">&lt;</span><span style="color: rgba(128, 0, 0, 1)">title</span><span style="color: rgba(0, 0, 255, 1)">&gt;</span>WebSocket 示例<span style="color: rgba(0, 0, 255, 1)">&lt;/</span><span style="color: rgba(128, 0, 0, 1)">title</span><span style="color: rgba(0, 0, 255, 1)">&gt;</span>
<span style="color: rgba(0, 0, 255, 1)">&lt;/</span><span style="color: rgba(128, 0, 0, 1)">head</span><span style="color: rgba(0, 0, 255, 1)">&gt;</span>
<span style="color: rgba(0, 0, 255, 1)">&lt;</span><span style="color: rgba(128, 0, 0, 1)">body</span><span style="color: rgba(0, 0, 255, 1)">&gt;</span>
    <span style="color: rgba(0, 0, 255, 1)">&lt;</span><span style="color: rgba(128, 0, 0, 1)">div</span><span style="color: rgba(0, 0, 255, 1)">&gt;</span>
      <span style="color: rgba(0, 0, 255, 1)">&lt;</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)">&gt;</span>
      <span style="color: rgba(0, 0, 255, 1)">&lt;</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)">&gt;</span>发送<span style="color: rgba(0, 0, 255, 1)">&lt;/</span><span style="color: rgba(128, 0, 0, 1)">button</span><span style="color: rgba(0, 0, 255, 1)">&gt;</span>
    <span style="color: rgba(0, 0, 255, 1)">&lt;/</span><span style="color: rgba(128, 0, 0, 1)">div</span><span style="color: rgba(0, 0, 255, 1)">&gt;</span>
    <span style="color: rgba(0, 0, 255, 1)">&lt;</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)">&gt;&lt;/</span><span style="color: rgba(128, 0, 0, 1)">div</span><span style="color: rgba(0, 0, 255, 1)">&gt;</span>

    <span style="color: rgba(0, 0, 255, 1)">&lt;</span><span style="color: rgba(128, 0, 0, 1)">script</span><span style="color: rgba(0, 0, 255, 1)">&gt;</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)">&lt;</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)">&gt;</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)">&lt;</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&gt; ${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)">&lt;/</span><span style="color: rgba(128, 0, 0, 1)">script</span><span style="color: rgba(0, 0, 255, 1)">&gt;</span>
<span style="color: rgba(0, 0, 255, 1)">&lt;/</span><span style="color: rgba(128, 0, 0, 1)">body</span><span style="color: rgba(0, 0, 255, 1)">&gt;</span>
<span style="color: rgba(0, 0, 255, 1)">&lt;/</span><span style="color: rgba(128, 0, 0, 1)">html</span><span style="color: rgba(0, 0, 255, 1)">&gt;</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 &amp;&amp; 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(() =&gt;<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 = () =&gt;<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) =&gt;<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) =&gt;<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 &lt; <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>=&gt;<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)">&lt;</span><span style="color: rgba(128, 0, 0, 1)">dependency</span><span style="color: rgba(0, 0, 255, 1)">&gt;</span>
    <span style="color: rgba(0, 0, 255, 1)">&lt;</span><span style="color: rgba(128, 0, 0, 1)">groupId</span><span style="color: rgba(0, 0, 255, 1)">&gt;</span>org.springframework.boot<span style="color: rgba(0, 0, 255, 1)">&lt;/</span><span style="color: rgba(128, 0, 0, 1)">groupId</span><span style="color: rgba(0, 0, 255, 1)">&gt;</span>
    <span style="color: rgba(0, 0, 255, 1)">&lt;</span><span style="color: rgba(128, 0, 0, 1)">artifactId</span><span style="color: rgba(0, 0, 255, 1)">&gt;</span>spring-boot-starter-websocket<span style="color: rgba(0, 0, 255, 1)">&lt;/</span><span style="color: rgba(128, 0, 0, 1)">artifactId</span><span style="color: rgba(0, 0, 255, 1)">&gt;</span>
<span style="color: rgba(0, 0, 255, 1)">&lt;/</span><span style="color: rgba(128, 0, 0, 1)">dependency</span><span style="color: rgba(0, 0, 255, 1)">&gt;</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&lt;?&gt; preSend(Message&lt;?&gt;<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&lt;String, Session&gt; sessions = <span style="color: rgba(0, 0, 255, 1)">new</span> ConcurrentHashMap&lt;&gt;<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&lt;String, String&gt; userSessionMap = <span style="color: rgba(0, 0, 255, 1)">new</span> ConcurrentHashMap&lt;&gt;<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> &amp;&amp;<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&lt;Session&gt; sessions = Collections.synchronizedSet(<span style="color: rgba(0, 0, 255, 1)">new</span> HashSet&lt;&gt;<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&lt;String&gt;<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>&nbsp;配置文件</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(() -&gt;<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&lt;?&gt;<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&lt;?&gt; message, Class&lt;?&gt;<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&lt;String, Object&gt;<span style="color: rgba(0, 0, 0, 1)"> redisTemplate(RedisConnectionFactory factory) {
      RedisTemplate</span>&lt;String, Object&gt; template = <span style="color: rgba(0, 0, 255, 1)">new</span> RedisTemplate&lt;&gt;<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&lt;&gt;(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&lt;String, Object&gt;<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]
查看完整版本: WebSocket 的使用