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

Java Socket 通信 Demo:雙向聊天室的完整實現與架構解析

     編輯:Java 相關知識

Socket 是網絡編程的基石。本文通過一個「雙向聊天室」完整 Demo,演示 TCP 長連接下的並發收發架構:主線程監聽鍵盤輸入發送,子線程循環接收對端消息。代碼可直接運行,適合初學者理解 Socket 全雙工通信模型。

架構設計

┌─────────────────────────────────────────────────────────────┐
│                        TalkMe (主類)                          │
├─────────────────────────────────────────────────────────────┤
│  main()                                                     │
│    ├─ listener() → Thread A: ServerSocket.accept() 循環接收 │
│    └─ talker(ip)  → Thread B: Socket 連接遠端 + 發送循環     │
└─────────────────────────────────────────────────────────────┘

完整可運行代碼

package demo.socket;

import java.io.*;
import java.net.*;

public class TalkMe {
    private static final int PORT = 9666;

    public static void main(String[] args) throws IOException {
        System.out.print("請輸入對方 IP: ");
        String peerIp = new BufferedReader(new InputStreamReader(System.in)).readLine();

        TalkMe app = new TalkMe();
        app.listener().start();           // 線程 A: 作為服務端監聽
        app.talker(peerIp).start();       // 線程 B: 作為客戶端連接
    }

    // 線程 A: 監聽對方連接並接收消息
    private Thread listener() {
        return new Thread(() -> {
            try (ServerSocket server = new ServerSocket(PORT)) {
                System.out.println("監聽中... 端口: " + PORT);
                try (Socket socket = server.accept();
                     BufferedReader in = new BufferedReader(
                         new InputStreamReader(socket.getInputStream(), StandardCharsets.UTF_8))) {
                    
                    System.out.println("連接建立: " + socket.getInetAddress());
                    String msg;
                    while ((msg = in.readLine()) != null) {
                        System.out.println("\n[對方] " + msg);
                        System.out.print("我: "); // 提示符
                    }
                }
            } catch (IOException e) {
                System.err.println("監聽異常: " + e.getMessage());
            }
        }, "Listener-Thread");
    }

    // 線程 B: 連接對方並發送鍵盤輸入
    private Thread talker(String peerIp) {
        return new Thread(() -> {
            try (Socket socket = new Socket(peerIp, PORT);
                 PrintWriter out = new PrintWriter(
                     new OutputStreamWriter(socket.getOutputStream(), StandardCharsets.UTF_8), true);
                 BufferedReader keyboard = new BufferedReader(
                     new InputStreamReader(System.in, StandardCharsets.UTF_8))) {
                
                System.out.println("連接成功: " + peerIp + ":" + PORT);
                System.out.print("我: ");
                
                String line;
                while ((line = keyboard.readLine()) != null) {
                    out.println(line); // 自動 flush
                    System.out.print("我: ");
                }
            } catch (UnknownHostException e) {
                System.err.println("未知主機: " + peerIp);
            } catch (IOException e) {
                System.err.println("通信異常: " + e.getMessage());
            }
        }, "Talker-Thread");
    }
}

運行步驟

  1. 雙方各運行一個實例:java demo.socket.TalkMe
  2. 甲輸入乙的 IP,乙輸入甲的 IP
  3. 雙方均可輸入文字按 Enter 發送,實時雙向通信

關鍵技術點解析

1. 全雙工通信:兩條獨立線程

  • Listener 線程:綁定本地端口 ServerSocket.accept() 阻塞等待連接,建立後循環 readLine() 接收
  • Talker 線程:主動 new Socket(ip, port) 連接遠端,循環讀取鍵盤 readLine() 並 println() 發送
  • 兩線程共享標準輸出,故加提示符 System.out.print("我: ") 區分輸入/輸出

2. 字符流包裝鏈:編碼顯式化

// 接收端
new BufferedReader(
    new InputStreamReader(socket.getInputStream(), StandardCharsets.UTF_8))

// 發送端
new PrintWriter(
    new OutputStreamWriter(socket.getOutputStream(), StandardCharsets.UTF_8), true) // true = autoFlush

關鍵:StandardCharsets.UTF_8 顯式指定編碼,避免跨平台亂碼;PrintWriter 第二參數 true 開啟自動刷新,println() 即發送。

3. try-with-resources 保證資源釋放

所有 Socket、流對象均在 try(...) 中聲明,異常/正常退出自動 close(),無洩漏。

常見問題與改進

問題原因改進
單機雙開端口衝突雙方用同一 PORT 監聽雙方協商不同端口,或改為一端監聽一端連接
NAT/防火牆不通公網 IP 不可直連端口映射、STUN/TURN、中繼服務器、VPN
消息粘包/半包TCP 字節流無邊界應用層協議:長度前綴 / 分隔符 / 序列化框架
異常時線程無提示catch 僅打印錯誤加入重連邏輯、心跳檢測、斷線重連

生產級架構演進

// 1. 協議層:定義消息格式
public record ChatMessage(String from, String content, long timestamp) {}

// 2. 編解碼器:JSON 序列化 + 長度前綴
public class MessageCodec {
    public byte[] encode(ChatMessage msg) { ... }
    public ChatMessage decode(byte[] data) { ... }
}

// 3. Netty 重構:事件驅動、非阻塞、管道處理
@ChannelHandler.Sharable
public class ChatClientHandler extends SimpleChannelInboundHandler {
    @Override protected void channelRead0(ChannelHandlerContext ctx, ChatMessage msg) {
        System.out.println("[" + msg.timestamp() + "] " + msg.from() + ": " + msg.content());
    }
}

總結

這個 Demo 展示了 Socket 通信的核心要素:ServerSocket 監聽、Socket 連接、流包裝、雙線程全雙工、編碼顯式化、資源自動管理。理解這些基礎,再學習 NIO、Netty、gRPC 等高級框架才能知其然更知其所以然。

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