首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> Java 程式設計 >> Java 相關知識

Java 後端整合 Tomcat 實現 WebSocket 即時通訊完整教學

     編輯:Java 相關知識

前言

傳統 HTTP 輪詢、Comet 技術在即時性、頻寬、伺服器資源上均有明顯短板。HTML5 WebSocket 提供真正的全雙工長連接,Java EE 7 (JSR-356) 標準化 API,Tomcat 7.0.47+ 完整支援。本文從原理到實戰,手把手教你構建高併發即時通訊系統。

核心原理速覽

  • 握手階段:客戶端發送 HTTP Upgrade 請求,伺服器回應 101 Switching Protocols,協定升級為 WebSocket。
  • 資料傳輸:建立 TCP 長連接,雙向推送二進制/文字幀,無 HTTP 頭部開銷。
  • 心跳機制:Ping/Pong 幀維持連接存活,穿透 NAT/防火牆。

專案依賴 (Maven)

<dependency>
    <groupId>javax.websocket</groupId>
    <artifactId>javax.websocket-api</artifactId>
    <version>1.1</version>
    <scope>provided</scope> <!-- Tomcat 內建實作 -->
</dependency>

伺服端實作

package com.example.websocket;

import javax.websocket.*;
import javax.websocket.server.ServerEndpoint;
import java.io.IOException;
import java.util.concurrent.CopyOnWriteArraySet;

@ServerEndpoint("/websocket")
public class ChatWebSocket {
    // 線程安全集合管理所有連接
    private static final CopyOnWriteArraySet<Session> sessions = new CopyOnWriteArraySet<>();

    @OnOpen
    public void onOpen(Session session) {
        sessions.add(session);
        broadcast("系統:新用戶加入,當前在線 " + sessions.size() + " 人");
    }

    @OnMessage
    public void onMessage(String message, Session sender) {
        broadcast("用戶 " + sender.getId() + ":" + message);
    }

    @OnClose
    public void onClose(Session session) {
        sessions.remove(session);
        broadcast("系統:用戶離開,當前在線 " + sessions.size() + " 人");
    }

    @OnError
    public void onError(Session session, Throwable error) {
        error.printStackTrace();
    }

    private void broadcast(String msg) {
        for (Session s : sessions) {
            if (s.isOpen()) {
                try {
                    s.getBasicRemote().sendText(msg); // 同步發送
                } catch (IOException e) {
                    sessions.remove(s); // 發送失敗移除
                }
            }
        }
    }
}

客戶端網頁 (HTML5 + JS)

<!DOCTYPE html>
<html>
<head><meta charset="UTF-8"><title>WebSocket Chat</title></head>
<body>
<div id="log" style="height:300px;overflow:auto;border:1px solid #ccc;padding:10px"></div>
<input id="msg" type="text" style="width:300px" placeholder="輸入訊息..." />
<button onclick="send()">發送</button>
<button onclick="closeWs()">斷開</button>

<script>
const log = document.getElementById('log');
const ws = new WebSocket('ws://localhost:8080/your-app/websocket');

ws.onopen = () => append('已連線');
ws.onmessage = e => append(e.data);
ws.onclose = () => append('連線關閉');
ws.onerror = e => append('錯誤:' + e);

function append(text) {
    log.innerHTML += '<div>' + text + '</div>';
    log.scrollTop = log.scrollHeight;
}
function send() {
    const input = document.getElementById('msg');
    if (input.value.trim()) ws.send(input.value), input.value = '';
}
function closeWs() { ws.close(); }
</script>
</body>
</html>

進階主題

1. 單點傳送 vs 廣播

使用 Map<String, Session> 以用戶 ID 為鍵,實現私聊:

@OnMessage
public void onMessage(String msg, Session session) {
    // 格式:@userId:內容
    if (msg.startsWith("@")) {
        String[] parts = msg.split(":", 2);
        String targetId = parts[0].substring(1);
        Session target = userMap.get(targetId);
        if (target != null) target.getAsyncRemote().sendText("私訊:" + parts[1]);
    } else {
        broadcast(msg);
    }
}

2. 編解碼器 (Encoder/Decoder)

自動處理 JSON ⇄ POJO 轉換:

public class MessageDecoder implements Decoder.Text<ChatMessage> {
    @Override public ChatMessage decode(String s) { return new Gson().fromJson(s, ChatMessage.class); }
    @Override public boolean willDecode(String s) { return true; }
    @Override public void init(EndpointConfig ec) {}
    @Override public void destroy() {}
}

// 註冊
@ServerEndpoint(value = "/websocket", decoders = MessageDecoder.class, encoders = MessageEncoder.class)

3. 整合 Spring 管理 Bean

  • 啟用 @ServerEndpoint 掃描:需配置 ServerEndpointExporter Bean。
  • 注入 Service:@Autowired 在 Endpoint 中需手動從 ApplicationContext 取得,因 Endpoint 非 Spring 管理。

4. 生產環境優化

項目建議
連接數擴展Nginx 反向代理 + sticky session 或 Redis Pub/Sub 跨節點廣播
心跳間隔客戶端 30s 發送 Ping,伺服端 web.xml 設定 websocket.idleTimeout
訊息大小限制@ServerEndpoint(maxTextMessageSize=65536)
安全性握手階段驗證 Token/Session,WSS (TLS) 加密傳輸

常見坑點排查

  1. 404 Not Found:確認 @ServerEndpoint 路徑與 JS 連接 URL 一致,且 web.xml metadata-complete="false" 或 Servlet 3.0+ 自動掃描。
  2. Tomcat 版本過低:需 7.0.47+ (JSR-356) 或 8.0+,否則缺少 javax.websocket 實作。
  3. 發送阻塞:高併發下改用 getAsyncRemote() 非同步發送,避免阻塞 I/O 執行緒。
  4. 序列化錯誤:自訂物件需實作 Encoder/Decoder,不可直接 sendObject。

結語

WebSocket 讓 Java 後端擁有原生即時推送能力,結合 JSR-356 標準 API 與 Tomcat 高效能 NIO 連接器,可輕鬆支撐萬級併發長連接。掌握生命週期註解、編解碼器、集群擴展與安全實務,即可構建企業級即時通訊骨幹。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved