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

Java 實作網路中國象棋室 (二):伺服端核心邏輯與協定設計深度解析

     編輯:Java 相關知識

專案架構回顧

本系列採用客戶端 Applet (棋盤渲染/走法判斷/用戶操作) + 伺服端 Java Application (狀態機/訊息路由/房間管理) 架構。通訊協定為自訂二進位封包 (Header + Body),基於 TCP 長連線,採用雙緩衝繪圖消除閃爍。本文聚焦伺服端核心類別:ChessServer、ChessUser、TcpDataReceiver/Sender、HttpInputStream。

通訊協定定義 (二進位封包)

欄位位移長度說明
CmdType01 byte指令類型 (0:進桌, 1:離桌, 2:走子, 3:求和, 4:認輸, 5:悔棋, 6:用戶列表, 7:新用戶, 8:用戶退出, 9:聊天, 10:登入回應)
DataLen1-22 bytes (short)Body 長度 (大端序)
Reserved3-42 bytes保留/擴充 (如桌號、顏色)
UserID5-84 bytes (int)發送者 ID (大端序)
Body12+DataLen bytes指令參數 (如走棋座標、聊天文字、用戶資料)

關鍵設計:固定 12 bytes Header 確保 HttpInputStream.readBytes() 能精確讀取完整封包,解決 TCP 黏包/半包問題。

核心類別職責詳解

1. ChessServer : 中心控制器 (單例模式)

  • 狀態管理:Hashtable userlist (全館用戶)、ChessTable[20] tb (20 張桌子)、ServerSocket ss。
  • 訊息分發:
    • sendToRoom():廣播給大廳所有用戶 (登入/退出/用戶列表)。
    • sendToTable():廣播給特定桌子用戶 (走棋/聊天/悔棋/求和/認輸)。
  • 桌位邏輯:addTableUser(tableID, userID, color) 分配紅/黑/觀戰座位,回傳實際狀態碼;removeTableUser() 清理。
  • 封包處理入口:onUserPackageArrived(header, len, data) 根據 CmdType 分派,同步鎖保護共享資料結構。

2. ChessUser : 用戶會話實體

  • 持有 Socket、TcpDataReceiver (讀取執行緒)、TcpDataSender (寫入工具)。
  • 實作 TcpDataListener,將收到的封包轉交 ChessServer.hwnd.onUserPackageArrived()。
  • 資料模型:username, userID, tableID, state (0紅走/1黑走/2觀紅/3觀黑)。
  • getBytes() 序列化用戶資訊 (8 bytes 名稱 + 4 bytes ID + 2 bytes 狀態) 供大廳列表廣播。

3. TcpDataReceiver : 專用讀取執行緒

public void run() {
    while (isrunning) {
        HttpInputStream.readBytes(is, 12, header); // 讀固定 Header
        int len = Convert.byteToShort(header[1], header[2]);
        if (len > 0) HttpInputStream.readBytes(is, len, data); // 讀 Body
        notifyListeners(header, len, len > 0 ? data : null);
    }
}
  • 繼承 Thread,持續阻塞讀取,解碼固定長度 Header + 可變 Body。
  • 觀察者模式:持有 Vector,收到完整封包回呼 onTcpDataArrived()。
  • 異常捕捉:IOException 視為連線斷開,上層負責清理。

4. TcpDataSender : 封裝寫入邏輯

  • sendData(header, body, len):依序寫入 Header、Body,flush() 確保發送。
  • sendLoginBack(userID):發送 CmdType=10 登入成功回應 (含分配的 UserID)。
  • sendUserListData(userListBytes):發送 CmdType=6 大廳用戶列表。

5. HttpInputStream : 精確位元組讀取工具

關鍵靜態方法 readBytes(InputStream, int length, byte[] dest):

public static void readBytes(InputStream in, int length, byte[] data) throws IOException {
    int n = 0;
    while (n < length) {
        int read = in.read(data, n, length - n);
        if (read == -1) throw new IOException("Connection closed prematurely");
        n += read;
    }
}

確保讀滿指定位元組數才返回,解決 InputStream.read() 可能不滿緩衝區就返回的特性。

6. Convert : 位元組序列化工具

  • intToBytes(int)、byteToInt(b1,b2,b3,b4) (大端序/Network Byte Order)。
  • shortToBytes(short)、byteToShort(b1,b2)。

登入與連線建立流程 (ChessLogin)

  1. ChessLogin.run() 迴圈 ss.accept() 接收新 Socket。
  2. chkLogin() 讀取 12 bytes Header + Body,解析用戶名 (Body 即用戶名位元組)。
  3. 建立 LoginEvent(userID, username, socket) 通知 ChessServer.onLoginEvent()。
  4. Server 建立 ChessUser,初始化收發器,發送登入回應 (含 UserID)、大廳用戶列表,廣播「新用戶進入」訊息。

關鍵設計亮點與潛在改進

亮點說明
固定長度 Header簡化解析邏輯,配合 HttpInputStream 徹底解決黏包
職責分離Server/User/Receiver/Sender/Protocol 各司其職,易於單元測試
觀察者模式解耦Receiver 不知業務邏輯,純粹負責解碼分發
雙緩衝繪圖 (Client)離屏渲染消除閃爍,提升棋盤操作流暢度

改進建議

  • 執行緒模型升級:目前每連線一讀取執行緒 (Receiver),高併發下建議遷移至 NIO Selector 或虛擬執行緒 (Java 21+)。
  • 協定擴充性:Header 預留擴充欄位,支援協定版本號、壓縮旗標、加密旗標。
  • 心跳/死連線檢測:定時發送 PING (CmdType=99),超時未收到 PONG 強制清理。
  • 序列化升級:Body 採用 Protobuf/FlatBuffers 替代手寫位元組操作,支援結構演進。
  • 安全性:引入 TLS (SSLSocket) 加密傳輸,防竄改/偷窺。
  • 資料庫持久化:棋譜、用戶積分、聊天記錄落地 MySQL/Redis。

總結

本象棋室伺服端展示了經典 Java TCP 伺服器架構:ServerSocket.accept() + 每連線專屬讀取執行緒 + 自訂二進位協定 (固定 Header + 可變 Body) + 觀察者模式分發 + 共享狀態同步鎖保護。雖技術棧屬上一代 (BIO、Applet、Swing),但其協定設計、模組解耦、狀態機管理思想在現代 Netty/gRPC 微服務架構中依然通用。建議以此為基礎,遷移至 Netty + Protobuf + WebSocket 實現瀏覽器原生跨平台對弈。

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