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

Java Socket 網絡編程深度實戰指南

     編輯:Java 程式設計入門

Java Socket 編程核心模型

Java Socket 編程圍繞兩大核心類展開:ServerSocket(服務端監聽)與 Socket(雙向通信端點)。服務端通過 ServerSocket 綁定端口監聽,調用 accept() 阻塞等待客戶端連接;連接建立後,雙方各持一個 Socket,通過其輸入/輸出流實現全雙工通信。

基礎通信模式:客戶端寫、服務端讀

服務端實現

public class Server {
    public static void main(String[] args) throws IOException {
        int port = 8899;
        ServerSocket server = new ServerSocket(port);
        Socket socket = server.accept(); // 阻塞等待連接
        
        Reader reader = new InputStreamReader(socket.getInputStream());
        char[] chars = new char[64];
        int len;
        StringBuilder sb = new StringBuilder();
        while ((len = reader.read(chars)) != -1) {
            sb.append(new String(chars, 0, len));
        }
        System.out.println("from client: " + sb);
        
        reader.close();
        socket.close();
        server.close();
    }
}

客戶端實現

public class Client {
    public static void main(String[] args) throws Exception {
        String host = "127.0.0.1";
        int port = 8899;
        Socket client = new Socket(host, port);
        
        Writer writer = new OutputStreamWriter(client.getOutputStream());
        writer.write("Hello Server.");
        writer.flush(); // 關鍵:刷新緩衝區確保數據發送
        writer.close();
        client.close();
    }
}

關鍵點:若寫操作後不關閉流而是進行其他阻塞操作,必須調用 flush(),否則會導致雙方無限等待。

雙向通信與終止標記協議

Socket 原生支持雙向通信,但流的阻塞特性要求雙方約定終止標記(如 "eof"),否則 read() 會永久阻塞導致後續邏輯無法執行。

改進版服務端:標記驅動的循環退出

while ((len = reader.read(chars)) != -1) {
    String temp = new String(chars, 0, len);
    if ((index = temp.indexOf("eof")) != -1) {
        sb.append(temp.substring(0, index));
        break; // 遇到終止標記跳出循環
    }
    sb.append(temp);
}
// 後續可繼續發送響應...
writer.write("Hello Client.");
writer.write("eof"); // 回應終止標記
writer.flush();

配套客戶端:雙向標記協議

writer.write("Hello Server.");
writer.write("eof"); // 發送終止標記
writer.flush();

// 讀取服務端響應
Reader reader = new InputStreamReader(client.getInputStream());
while ((len = reader.read(chars)) != -1) {
    String temp = new String(chars, 0, len);
    if ((index = temp.indexOf("eof")) != -1) {
        sb.append(temp.substring(0, index));
        break;
    }
    sb.append(temp);
}

多客戶端併發架構演進

階段一:同步阻塞模型(單線程循環)

服務端通過 while(true) 循環不斷 accept(),但同一時刻只能處理一個客戶端,併發性能差。

階段二:每請求一線程模型

while (true) {
    Socket socket = server.accept();
    new Thread(new Task(socket)).start(); // 每連接啟動獨立線程
}

static class Task implements Runnable {
    private Socket socket;
    public Task(Socket socket) { this.socket = socket; }
    public void run() {
        try { handleSocket(); } 
        catch (Exception e) { e.printStackTrace(); }
    }
    private void handleSocket() throws Exception {
        // 具體通信邏輯...
    }
}

實現了併發處理,但線程創建銷毀開銷大,高併發下易耗盡資源。

BufferedReader 優化行讀取

利用 BufferedReader.readLine() 替換字符數組讀取,自動識別換行符(\n/\r\n)或流結束符。注意:寫入端必須發送換行符並 flush(),否則 readLine() 永久阻塞。

服務端行讀取版

BufferedReader br = new BufferedReader(
    new InputStreamReader(socket.getInputStream()));
String temp;
while ((temp = br.readLine()) != null) {
    if (temp.indexOf("eof") != -1) break;
    // 處理邏輯...
}
writer.write("Hello Client.\n"); // 必須包含換行符
writer.write("eof\n");
writer.flush();

超時控制:防止無限阻塞

Socket.setSoTimeout(毫秒) 設置讀操作超時時間。超過指定時間未讀到數據將拋出 SocketTimeoutException,適用於同步調用場景的熔斷保護。

客戶端超時示例

Socket client = new Socket(host, port);
client.setSoTimeout(10 * 1000); // 10秒超時

BufferedReader br = new BufferedReader(
    new InputStreamReader(client.getInputStream()));
try {
    while ((temp = br.readLine()) != null) { /* ... */ }
} catch (SocketTimeoutException e) {
    System.out.println("數據讀取超時。");
}

編碼一致性:杜絕中文亂碼

亂碼本質是發送端編碼與接收端解碼不一致。雙方必須顯式指定相同字符集(如 UTF-8 或 GBK)。

服務端指定編碼

// 輸入流用 GBK 讀取
BufferedReader br = new BufferedReader(
    new InputStreamReader(socket.getInputStream(), "GBK"));
// 輸出流用 UTF-8 發送
Writer writer = new OutputStreamWriter(
    socket.getOutputStream(), "UTF-8");

客戶端對應配置

// 發送用 GBK
Writer writer = new OutputStreamWriter(
    client.getOutputStream(), "GBK");
// 接收用 UTF-8
BufferedReader br = new BufferedReader(
    new InputStreamReader(client.getInputStream(), "UTF-8"));

生產環境建議統一使用 UTF-8,並在協議層面明確約定編碼標準。

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