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

Java Socket 網絡編程實戰:TCP 客戶端/服務端完整開發指南

     編輯:Java 相關知識

前言:Socket 是什麼?

Socket(套接字)是計算機網絡通信的基礎設施。瀏覽器、即時通訊、P2P 下載等幾乎所有網絡應用都建立在 Socket 之上。本文聚焦 基於 TCP/IP 的流式 Socket 編程,涵蓋客戶端/服務端完整開發流程。

一、Unix I/O 模型與 Socket 本質

  • Unix 哲學:一切皆文件 → Open/Read/Write/Close 統一接口
  • 文件描述符:指向打開文件/設備/通信通道的整數句柄
  • Socket 文件描述符:網絡通信端點,支持跨進程/跨主機通信
  • BSD Socket API:TCP/UDP 之上的標準化接口層

二、TCP vs UDP:協議選型決策表

維度TCP(流式)UDP(數據報)
連接性面向連接(三次握手)無連接
可靠性可靠、有序、無重複不可靠、無序、可能丟包/重複
數據邊界無邊界(流式),需應用層分幀保留消息邊界
大小限制無實際限制單數據報 ≤ 64KB
開銷較大(連接建立/維護)極小
適用場景HTTP、FTP、SSH、郵件、數據庫DNS、視頻流、即時遊戲、廣播

選型原則:數據完整性關鍵 → TCP;實時性優先、可容忍丟包 → UDP。

三、Java Socket API 核心類

全部位於 java.net 包:

  • Socket:客戶端套接字
  • ServerSocket:服務端監聽套接字
  • InetAddress:IP 地址封裝
  • InetSocketAddress:IP + 端口組合

端口選擇:0~1023 為系統保留(HTTP 80、HTTPS 443、SSH 22 等),自定義服務請使用 1024 以上。

四、客戶端開發標準流程


public class TcpClient {
    public static void main(String[] args) {
        String host = "127.0.0.1";
        int port = 8919;
        
        try (Socket socket = new Socket(host, port);
             Writer writer = new OutputStreamWriter(socket.getOutputStream(), StandardCharsets.UTF_8)) {
            
            // 發送數據
            writer.write("Hello from Client!");
            writer.flush();  // 關鍵:強制發送緩衝區數據
            
            // 如需接收響應
            try (Reader reader = new InputStreamReader(socket.getInputStream(), StandardCharsets.UTF_8)) {
                char[] buffer = new char[1024];
                int len = reader.read(buffer);
                if (len > 0) System.out.println("Server reply: " + new String(buffer, 0, len));
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

關鍵點:

  1. 使用 try-with-resources 自動關閉資源(JDK 7+)
  2. 指定字符集避免亂碼
  3. flush() 確保數據發送,否則可能滯留緩衝區

五、服務端開發標準流程


public class TcpServer {
    public static void main(String[] args) {
        int port = 8919;
        
        try (ServerSocket server = new ServerSocket(port)) {
            System.out.println("Server listening on port " + port);
            
            while (true) {  // 持續接受客戶端
                try (Socket client = server.accept();  // 阻塞等待連接
                     Reader reader = new InputStreamReader(client.getInputStream(), StandardCharsets.UTF_8);
                     Writer writer = new OutputStreamWriter(client.getOutputStream(), StandardCharsets.UTF_8)) {
                    
                    // 讀取完整消息
                    StringBuilder msg = new StringBuilder();
                    char[] buffer = new char[1024];
                    int len;
                    while ((len = reader.read(buffer)) != -1) {
                        msg.append(buffer, 0, len);
                    }
                    System.out.println("Received: " + msg);
                    
                    // 回複確認
                    writer.write("ACK: " + msg);
                    writer.flush();
                } catch (IOException e) {
                    System.err.println("Client handling error: " + e.getMessage());
                }
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

關鍵點:

  1. server.accept() 阻塞直到客戶端連接,返回專用 Socket
  2. 單線程模型:一次只能處理一個客戶端,生產環境需引入線程池/NIO
  3. 讀取循環直到 read() == -1(對端關閉輸出流)

六、實戰案例:回聲服務器


public class EchoServer {
    public static void main(String[] args) {
        try (ServerSocket server = new ServerSocket(9999)) {
            System.out.println("Echo server started on 9999");
            while (true) {
                try (Socket client = server.accept();
                     BufferedReader in = new BufferedReader(new InputStreamReader(client.getInputStream()));
                     PrintWriter out = new PrintWriter(client.getOutputStream(), true)) {  // autoFlush=true
                    
                    String line;
                    while ((line = in.readLine()) != null) {
                        System.out.println("Echo: " + line);
                        out.println(line);  // 原樣返回
                    }
                }
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

測試:


$ telnet 127.0.0.1 9999
Trying 127.0.0.1...
Connected to 127.0.0.1.
Escape character is '^]'.
hello
hello
world
world

七、進階主題:生產環境必備優化

1. 多線程/線程池模型


ExecutorService pool = Executors.newFixedThreadPool(50);
while (true) {
    Socket client = server.accept();
    pool.submit(() -> handleClient(client));
}

2. NIO 非阻塞模型

  • ServerSocketChannel + Selector 多路複用
  • 單線程處理萬級連接(Netty 底層原理)

3. 應用層協議設計

TCP 無消息邊界,必須自定義協議:

  • 定長消息:固定字節數
  • 分隔符:換行符、特殊字符(如回聲服務器)
  • 長度前綴:4 字節長度 + 消息體(推薦,高性能解析)
  • 序列化協議:Protobuf、Avro、JSON over TCP

4. 優雅關閉


// 客戶端半關閉:發送完數據關閉輸出流,但保留輸入流接收響應
socket.shutdownOutput();  
// 服務端檢測到 read() == -1 後處理完業務再關閉

5. 常見坑與排查

現象原因解決
客戶端發送無響應未 flush / 服務端未讀取完檢查 flush、協議解析邏輯
中文亂碼編解碼字符集不一致統一指定 UTF-8
連接重置對端異常關閉 / 防火牆捕獲 ConnectionResetException
地址已在使用端口未釋放 / TIME_WAITSO_REUSEADDR / 等待 2MSL

八、Java 網絡編程最佳實踐清單

  1. 優先使用 java.net 標準 API,避免 sun.* 內部類
  2. 生產環境引入 Netty 等成熟框架,勿重複造輪子
  3. 設置連接/讀寫超時:socket.setSoTimeout(ms)
  4. 啟用 TCP_NODELAY(關閉 Nagle 算法)降低小包延遲
  5. 合理配置發送/接收緩衝區:setSendBufferSize/setReceiveBufferSize
  6. 記錄關鍵網絡日誌:連接建立、斷開、異常、耗時統計

Socket 編程是分布式系統的基石。掌握 TCP 流式通信本質、協議設計原則、同步阻塞與異步非阻塞模型,配合 Netty 等工業級框架,即可構建高性能、可靠的網絡應用。建議進階學習:SSL/TLS 安全通信、HTTP/2 多路複用、gRPC RPC 框架、WebSocket 全雙工通信。

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