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

Java Socket 程式設計完整指南:從基礎通訊到多線程併發處理

     編輯:Java 相關知識

Java Socket 程式設計核心概念

在 Java 網路程式設計中,Socket 和 ServerSocket 是兩個核心類別。ServerSocket 負責在伺服器端監聽特定埠號,當客戶端發起連線請求時,透過 accept() 方法建立連線,並產生一個對應的 Socket 實例雙向通訊。這樣雙方各持有一個 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[] buffer = new char[64];
        int len;
        StringBuilder sb = new StringBuilder();
        while ((len = reader.read(buffer)) != -1) {
            sb.append(new String(buffer, 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(); // 必須 flush 確保資料送出
        writer.close();
        client.close();
    }
}

關鍵提醒:若寫入後不關閉流,而是進行阻塞式讀取,務必呼叫 flush(),否則雙方可能陷入互相等待的死結。

雙向通訊與結束標記機制

Socket 本質是全雙工的,但若伺服器端先阻塞讀取,再嘗試寫入回應,會導致寫入程式碼永遠無法執行。解決方案是約定「結束標記」(如 eof),讀取端偵測到標記即跳出迴圈,繼續後續邏輯。

改良版伺服器端(支援雙向)

while ((len = reader.read(chars)) != -1) {
    String temp = new String(chars, 0, len);
    if (temp.indexOf("eof") != -1) {
        sb.append(temp.substring(0, temp.indexOf("eof")));
        break;
    }
    sb.append(temp);
}
Writer writer = new OutputStreamWriter(socket.getOutputStream());
writer.write("Hello Client.");
writer.flush();

對應客戶端調整

writer.write("Hello Server.");
writer.write("eof");
writer.flush();
// 然後進行讀取...

多客戶端併發處理:多線程架構

單執行緒伺服器一次只能服務一個客戶端。引入多線程可實現併發:主線程循環 accept(),每新增一個連線即啟動新執行緒處理該 Socket。

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() {
        // 處理讀寫邏輯,同上述雙向通訊
    }
}

使用 BufferedReader 提升讀取效率

BufferedReader.readLine() 以換行符為邊界讀取,適合行導向協議。注意:發送端必須發送換行符(\n)並 flush(),否則接收端會持續阻塞。

BufferedReader br = new BufferedReader(new InputStreamReader(socket.getInputStream()));
String line;
while ((line = br.readLine()) != null) {
    if (line.contains("eof")) break;
    // 處理每一行
}

設定讀取超時:避免無限阻塞

透過 socket.setSoTimeout(毫秒) 設定讀取超時。超過時間未收到資料會拋出 SocketTimeoutException,適用於同步請求場景。

client.setSoTimeout(10 * 1000); // 10 秒
try {
    while ((line = br.readLine()) != null) { ... }
} catch (SocketTimeoutException e) {
    System.out.println("資料讀取超時");
}

字元編碼不一致導致亂碼問題

傳輸雙方必須使用相同編碼。例如伺服器端以 GBK 讀取、UTF-8 寫入,客戶端則須對應 GBK 寫入、UTF-8 讀取,否則中文會出現亂碼。

// 伺服器端指定編碼
BufferedReader br = new BufferedReader(new InputStreamReader(socket.getInputStream(), "GBK"));
Writer writer = new OutputStreamWriter(socket.getOutputStream(), "UTF-8");

// 客戶端對應調整
Writer writer = new OutputStreamWriter(client.getOutputStream(), "GBK");
BufferedReader br = new BufferedReader(new InputStreamReader(client.getInputStream(), "UTF-8"));

總結

Java Socket 程式設計涵蓋連線建立、阻塞 I/O 處理、協議設計(結束標記/換行符)、多線程併發、超時控制與編碼一致性。掌握這些核心要點,即可構建穩健的網路應用。

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