承接上一篇對自動升級模組架構與設定檔的介紹,本文將深入剖析核心 Java 原始碼的實作細節。這套自動升級系統採用 Socket 通訊,透過自定義協議類 AUPD 定義指令集,並利用 dom4j 解析 XML 版本資訊,實現客戶端與伺服器端的高效互動。
AUPD 類集中定義了客戶端與伺服器端交互的所有指令常數與資料傳輸參數,採用「指令 + 資料區」的二進制協議格式,確保傳輸效率與解析簡易性。
public class AUPD {
// 無意義操作
public static final String NONE = "NONE ";
// 版本資訊交換
public static final String SEND_CLIENT_VERSION = "SENDCVER"; // 客戶端發送版本
public static final String RECEIVED_CLIENT_VERSION = "RECDCVER"; // 伺服器確認接收
// 檔案路徑傳輸
public static final String SEND_FILE_ABSOULT = "SENDFILE"; // 發送檔案絕對路徑
public static final String RECEIVED_FILE_ABSOULT = "RECDFILE"; // 確認接收路徑
// 傳輸控制
public static final String START_TRANSMIT = "STARTTSM"; // 開始傳輸
public static final String TERMINATE_TRANSMIT = "TERMTSMT"; // 結束傳輸
// 狀態回報
public static final String UPDATED_FAILURE = "UPDEFAIL"; // 更新失敗
public static final String UPDATED_SUCCESSFUL = "UPDESUCC"; // 更新成功
public static final String NOTNEED_UPDATED = "NNEEDUPD"; // 無需更新
public static final String READY_TO_UPDATE = "READYTUP"; // 準備好接收更新
public static final String BYE = "BYEBYEOK"; // 結束連線
}
// 資料區偏移量(指令長度 8 位元組,實際协议头为 5 字节)
public static final int DATA_OFFSET = 5;
// 單次傳輸資料塊大小:1KB
public static final int DATA_SIZE = 1024;
// 發送緩衝區大小 = 資料塊 + 標誌位(1 byte) + 長度欄位(4 bytes)
public static final int BUFFER_SIZE = DATA_SIZE + 1 + 4;
// 封包類型標識
public static final int MARK_DATA_SECT = 0; // 資料段
public static final int CMD_DATA_SECT = 1; // 指令段
public static final int MARK_DATA_END = 127; // 資料結束標識
此設計將指令與資料明確區分,BUFFER_SIZE 的計算考慮了協議頭開銷,確保單次 Socket 讀寫操作能完整承載一個邏輯封包,減少拆包/黏包處理的複雜度。
AutoUpdateServer 繼承 Thread,啟動時讀取配置檔 Config 中的埠號,建立 ServerSocket 進入無限循環監聽。每當有客戶端連入,即建立服務執行緒 AUpdSrvProc 處理具體升級邏輯,主執行緒立即返回繼續監聽,實現併發服務。
public class AutoUpdateServer extends Thread {
private int port = 0;
private Config config = Config.getInstance();
private ServerSocket srvSocket = null;
public AutoUpdateServer() {
port = Integer.parseInt(config.getServerPort());
try {
srvSocket = new ServerSocket(port);
this.start(); // 啟動 run() 監聽循環
Config.print("自動更新伺服器在埠口 '" + port + "' 監聽");
} catch (IOException e) {
e.printStackTrace();
}
}
@Override
public void run() {
try {
while (true) {
Socket clSocket = null;
try {
clSocket = srvSocket.accept(); // 阻塞等待連線
Config.print("客戶端 '" + clSocket.getInetAddress() + "' 連線成功");
// 交由服務執行緒處理
AUpdSrvProc srvP = new AUpdSrvProc(clSocket);
srvP.start();
} catch (IOException ioe) {
// 連線異常處理
if (clSocket != null) clSocket.close();
Config.print("處理客戶端錯誤: " + ioe);
}
}
} catch (Exception e) {
Config.print("伺服器執行錯誤: " + e);
} finally {
try { if (srvSocket != null) srvSocket.close(); } catch (IOException ignored) {}
}
}
public static void main(String[] args) {
new AutoUpdateServer(); // 單例啟動
}
}
accept(),業務邏輯完全委派給 AUpdSrvProc,支援高併發。Config 讀取埠號、路徑等參數,便於部署調整。finally 區塊確保 ServerSocket 關閉,防止埠口佔用。需將 dom4j 與 jaxen 庫納入類路徑:
# 伺服器端啟動
java -cp .;.\bin;.\lib\dom4j-1.6.1.jar;.\lib\jaxen-1.1-beta-4.jar com.icbc.autoupdate.AutoUpdateServer
# 客戶端啟動
java -cp ./bin;./lib/jaxen-1.1-beta-4.jar;./lib/dom4j-1.6.1.jar com.icbc.autoupdate.AutoUpdateClient
下一篇(三)將詳細解析 AUpdSrvProc 服務執行緒邏輯、ClientVerParser 版本比對演算法,以及客戶端 AutoUpdateClient 的請求流程與檔案接收實作。