本專案示範如何使用 Java Socket、多執行緒、序列化物件傳輸與 Swing GUI 建立一個功能完整的聊天室,包含註冊/登入、群發/私訊、上下線通知、抖動視窗、檔案傳輸等核心功能。
ContextDemo implements Serializable) 封裝所有訊息類型。ServerSocket 監聽,每個客戶端連線啟動一條 Server_Thread 處理業務邏輯,HashMap<String, Socket> 管理在線用戶。Client_Thread 持續監聽伺服器推送訊息。public class ContextDemo implements Serializable {
private int type; // 訊息類型
private HashSet<String> clients; // 目標用戶集合
private String info; // 訊息內容
private String name; // 發送者
private String time; // 時間戳
private String last; // 檔案副檔名
private ArrayList<Byte> list2; // 檔案位元組資料
// Getter/Setter 省略
}
public static ServerSocket ss;
public static HashMap<String, Socket> online = new HashMap<>();
static {
try {
ss = new ServerSocket(8080);
System.out.println("伺服器已啟動,監聽 8080 埠...");
} catch (IOException e) { e.printStackTrace(); }
}
public void action() {
while (true) {
Socket s = ss.accept();
new Server_Thread(s).start(); // 每客戶端一條執行緒
}
}
// 廣播給所有在線用戶
public void sendtoall(ContextDemo ctx) {
for (Socket socket : online.values()) {
try (ObjectOutputStream oos = new ObjectOutputStream(socket.getOutputStream())) {
oos.writeObject(ctx);
oos.flush();
} catch (IOException e) { e.printStackTrace(); }
}
}
// 發送給指定用戶 (關鍵:每次新建 ObjectOutputStream 避免流衝突)
public void sendtothis(ContextDemo ctx) {
HashSet<String> targets = ctx.getClients();
for (String name : online.keySet()) {
if (targets.contains(name)) {
Socket s = online.get(name);
try (ObjectOutputStream oos = new ObjectOutputStream(s.getOutputStream())) {
oos.writeObject(ctx);
oos.flush();
} catch (IOException e) { e.printStackTrace(); }
}
}
}
@Override
public void run() {
try (ObjectInputStream ois = new ObjectInputStream(socket.getInputStream())) {
while (true) {
ContextDemo ctx = (ContextDemo) ois.readObject();
switch (ctx.getType()) {
case 0: handleOnline(ctx); break; // 上線
case 1: handleMessage(ctx); break; // 發送訊息
case 2: handleOffline(ctx); return; // 下線 (return 結束執行緒)
case 3: handleShake(ctx); break; // 抖動
case 4: handleFile(ctx); break; // 檔案傳輸
}
}
} catch (IOException | ClassNotFoundException e) { e.printStackTrace(); }
}
使用 Users.properties 儲存帳密,登入成功後建立 Socket 連線並進入聊天室視窗 (ChatRoom)。
Client_Thread 監聽。online Map,組裝「上線通知」+「在線名單」廣播給所有客戶端。JList 顯示在線用戶,並在訊息區顯示「XXX 上線啦!」JList 選中項目 (未選則視為群發),封裝 type=1 發送。sendtothis 轉發給目標用戶。JFileChooser 選檔 → 讀入 ArrayList<Byte> → 封裝 type=4 含副檔名、位元組陣列發送。ByteArrayInputStream + BufferedOutputStream 寫入磁碟 (sourse/dedao.副檔名)。public void shakeFrame() {
int x = getX(), y = getY();
for (int i = 0; i < 20; i++) {
if ((i & 1) == 0) { x += 8; y += 8; }
else { x -= 8; y -= 8; }
setLocation(x, y);
try { Thread.sleep(50); } catch (InterruptedException ignored) {}
}
}
ContextDemo 承載所有業務類型,擴充容易。ObjectOutputStream,否則多目標發送會導致流衝突 (作者耗時 10 小時除錯)。System.exit(0) 退出 JVM。