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

Java 網絡編程基礎 (二):Socket 類完整指南——客戶端連接、流處理與實戰技巧

     編輯:Java 相關知識

Socket 是 TCP 客戶端的核心類,封裝「連接建立、雙向流通信、連接屬性查詢」。本文詳細講解:構造器選擇、輸入輸出流包裝鏈、關鍵屬性獲取方法、完整客戶端實戰代碼、常見坑點與最佳實踐。

兩大核心構造器

Socket(InetAddress address, int port)Socket(String host, int port)
構造器參數適用場景
已有 InetAddress 對象,避免重複解析高性能、批量連接、已解析地址
直接用主機名/IP字串,內部自動解析簡單直觀,最常用

兩者均會:阻塞直到三次握手完成,成功返回已連接 Socket;失敗拋 IOException (連接超時/拒絕/網絡不可達)。

流包裝鏈:從字節流到字符流

Socket socket = new Socket("example.com", 8080);

// 1. 原始字節流
InputStream rawIn = socket.getInputStream();
OutputStream rawOut = socket.getOutputStream();

// 2. 字符流包裝 (指定編碼!)
BufferedReader in = new BufferedReader(
    new InputStreamReader(rawIn, StandardCharsets.UTF_8));
PrintWriter out = new PrintWriter(
    new OutputStreamWriter(rawOut, StandardCharsets.UTF_8), true); // autoFlush

// 3. 業務收發
out.println("GET / HTTP/1.1");
out.println("Host: example.com");
out.println(); // 空行結束請求頭
String responseLine;
while ((responseLine = in.readLine()) != null) {
    System.out.println(responseLine);
}

關鍵點:

  • StandardCharsets.UTF_8 顯式編碼,跨平台無亂碼
  • BufferedReader.readLine() 按行讀取,適配文本協議 (HTTP, SMTP, 自定義行制協議)
  • PrintWriter 第二參數 true = autoFlush,println() 立即發送
  • 關閉順序:先關包裝流 (會級聯關閉底層流) → 再關 Socket

Socket 關鍵屬性獲取方法

Socket socket = new Socket("example.com", 8080);

System.out.println("本地地址: " + socket.getLocalAddress().getHostAddress()); // 客戶端 IP
System.out.println("本地端口: " + socket.getLocalPort());                     // 客戶端臨時端口
System.out.println("遠程地址: " + socket.getInetAddress().getHostAddress());   // 服務端 IP
System.out.println("遠程端口: " + socket.getPort());                           // 服務端端口
System.out.println("是否連接: " + socket.isConnected());
System.out.println("是否綁定: " + socket.isBound());
System.out.println("是否關閉: " + socket.isClosed());
System.out.println("是否輸入關閉: " + socket.isInputShutdown());
System.out.println("是否輸出關閉: " + socket.isOutputShutdown());

// 進階配置
socket.setSoTimeout(5000);        // 讀取超時 5ms,超時拋 SocketTimeoutException
socket.setTcpNoDelay(true);       // 關閉 Nagle 算法,降低延遲 (適用小包高頻)
socket.setKeepAlive(true);        // 開啟 TCP KeepAlive (需 OS 支援,通常 2h 間隔)
socket.setSendBufferSize(64*1024); // 發送緩衝區
socket.setReceiveBufferSize(64*1024); // 接收緩衝區
socket.setTrafficClass(0x10);     // QoS: 低延遲/高吞吐等 (IP_TOS)

完整健壯客戶端範例

public class RobustTcpClient {
    private static final Logger log = LoggerFactory.getLogger(RobustTcpClient.class);

    public static void main(String[] args) {
        String host = "127.0.0.1";
        int port = 8080;
        int maxRetries = 3;
        int retryDelayMs = 1000;

        for (int attempt = 1; attempt <= maxRetries; attempt++) {
            try (Socket socket = new Socket()) {
                // 連接帶超時
                socket.connect(new InetSocketAddress(host, port), 5000);
                socket.setSoTimeout(10000); // 讀取超時
                
                log.info("連接成功: {}:{} (本地: {}:{})", 
                    host, port, socket.getLocalAddress(), socket.getLocalPort());

                try (BufferedReader in = new BufferedReader(
                            new InputStreamReader(socket.getInputStream(), StandardCharsets.UTF_8));
                     PrintWriter out = new PrintWriter(
                            new OutputStreamWriter(socket.getOutputStream(), StandardCharsets.UTF_8), true)) {
                    
                    // 業務邏輯
                    out.println("HELLO");
                    String resp = in.readLine();
                    log.info("響應: {}", resp);
                }
                break; // 成功跳出重試循環
            } catch (SocketTimeoutException e) {
                log.warn("連接/讀取超時 (嘗試 {}/{}): {}", attempt, maxRetries, e.getMessage());
            } catch (ConnectException e) {
                log.warn("連接被拒絕 (嘗試 {}/{}): {}", attempt, maxRetries, e.getMessage());
            } catch (IOException e) {
                log.error("IO 異常 (嘗試 {}/{}): {}", attempt, maxRetries, e.getMessage(), e);
            }
            if (attempt < maxRetries) {
                try { Thread.sleep(retryDelayMs); } catch (InterruptedException ignored) {}
            }
        }
    }
}

常見坑點總結

坑點後果解法
不指定編碼用平台默認跨平台/容器化部署亂碼強制 StandardCharsets.UTF_8
無讀取超時對端掛起導致線程永久阻塞socket.setSoTimeout(ms)
關閉 Socket 前未 flush發送緩衝區數據丟失PrintWriter(..., true) 或手動 flush()
單線程循環處理多客戶端併發能力極弱服務端用線程池/NIO/Netty
忽略半包/粘包協議解析錯誤應用層定義幀邊界 (長度/分隔符/序列化)

總結

Socket 是 TCP 客戶端的「瑞士軍刀」。掌握:構造器選擇、流包裝鏈 (編碼/緩衝/自動刷新)、屬性查詢、超時/KeepAlive/Nagle 配置、健壯重試模式,即可編寫生產級 TCP 客戶端代碼。

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