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

Java Socket 套接字編程完全指南:從基礎到連接池實戰

     編輯:Java 相關知識

什麼是套接字?

套接字為兩台計算機之間的通信提供了一種機制。在 Java 語言出現之前,套接字已廣泛應用於網絡編程;Java 將底層作業系統的複雜細節封裝起來,讓開發者能輕鬆高效地使用套接字進行網絡通信。

客戶端/服務端模型

這是一種經典的應用程序開發框架,將數據的表示與其內部處理、存儲分離。就像你在餐廳點披薩:你(客戶端)發送請求,廚房(服務端)處理並制作,服務員(網絡通信)將成品送達。你無需關心廚房內部細節,只需等待結果。一個服務端可以同時為多個客戶端服務,且兩者不一定是不同的硬體設備,也可以是同一台機器上的不同進程。

網絡協議:通信的規則

計算機通信同人類對話一樣,需要遵循規則(協議)。常見協議包括 TCP/IP、UDP 等。Java 提供豐富的網絡類庫,支持兩種通信工具:基於 UDP 的數據報和基於 TCP/IP 的 Socket。TCP 提供可靠、有序、點對點的字節流通道,適合大多數應用場景;UDP 則不保證送達,僅適合少量、短距離、非關鍵數據傳輸。

Socket 核心類與工作原理

Java 通過 java.net.Socket(客戶端套接字)和 java.net.ServerSocket(服務端監聽套接字)封裝網絡連接。Socket 內部使用輸入/輸出流進行數據讀寫,屏蔽了 TCP/IP 協議棧的封包、解包等底層細節,這也是它被稱為「流套接字」的原因。可以把 Socket 想象成電話聽筒:雙方建立連接後,通過專用通道對話,掛斷前線路始終佔用。

IP 地址與端口號

TCP 連接需要兩個關鍵信息:IP 地址(32 位邏輯地址,如 192.168.0.110)和端口號(標識具體服務,如 HTTP 默認 80,FTP 21)。域名系統(DNS)負責將域名解析為 IP 地址,方便用戶記憶。

實戰:構建 Socket 客戶端

以下示例展示如何編寫一個請求遠程文件內容的客戶端:

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

public class RemoteFileClient {
    private BufferedReader socketReader;
    private PrintWriter socketWriter;
    private String hostIp;
    private int hostPort;

    public RemoteFileClient(String hostIp, int hostPort) {
        this.hostIp = hostIp;
        this.hostPort = hostPort;
    }

    public void setUpConnection() throws IOException {
        Socket client = new Socket(hostIp, hostPort);
        socketReader = new BufferedReader(new InputStreamReader(client.getInputStream()));
        socketWriter = new PrintWriter(client.getOutputStream());
    }

    public String getFile(String fileName) throws IOException {
        StringBuilder content = new StringBuilder();
        socketWriter.println(fileName);
        socketWriter.flush();
        String line;
        while ((line = socketReader.readLine()) != null) {
            content.append(line).append("\n");
        }
        return content.toString();
    }

    public void tearDownConnection() throws IOException {
        socketWriter.close();
        socketReader.close();
    }

    public static void main(String[] args) throws IOException {
        RemoteFileClient client = new RemoteFileClient("127.0.0.1", 1001);
        client.setUpConnection();
        System.out.println(client.getFile("RemoteFileServer.java"));
        client.tearDownConnection();
    }
}

關鍵步驟:1) 用 IP 和端口實例化 Socket;2) 獲取輸入/輸出流並包裝為 BufferedReader/PrintWriter;3) 讀寫數據;4) 關閉流釋放資源。

實戰:構建基礎 Socket 服務端

服務端使用 ServerSocket 監聽端口,接受連接後為每個客戶端創建 Socket 進行通信:

public class RemoteFileServer {
    private int listenPort;

    public RemoteFileServer(int listenPort) {
        this.listenPort = listenPort;
    }

    public void acceptConnections() throws IOException {
        ServerSocket server = new ServerSocket(listenPort);
        while (true) {
            Socket incoming = server.accept();
            handleConnection(incoming);
        }
    }

    private void handleConnection(Socket client) throws IOException {
        BufferedReader in = new BufferedReader(new InputStreamReader(client.getInputStream()));
        PrintWriter out = new PrintWriter(client.getOutputStream());
        String filePath = in.readLine();
        BufferedReader fileIn = new BufferedReader(new FileReader(filePath));
        String line;
        while ((line = fileIn.readLine()) != null) {
            out.println(line);
        }
        fileIn.close();
        out.close();
        in.close();
    }
}

注意:此為單線程模型,同一時間只能處理一個請求,`handleConnection` 會阻塞後續連接。

進階:多線程服務端

為支持並發,將每個連接交給獨立線程處理:

public class MultiThreadedServer {
    public void acceptConnections() throws IOException {
        ServerSocket server = new ServerSocket(1001, 5); // backlog=5
        while (true) {
            Socket client = server.accept();
            new Thread(new ConnectionHandler(client)).start();
        }
    }
}

class ConnectionHandler implements Runnable {
    private Socket socket;
    public ConnectionHandler(Socket socket) { this.socket = socket; }

    public void run() {
        try (BufferedReader in = new BufferedReader(new InputStreamReader(socket.getInputStream()));
             PrintWriter out = new PrintWriter(socket.getOutputStream())) {
            String file = in.readLine();
            try (BufferedReader fileIn = new BufferedReader(new FileReader(file))) {
                String line;
                while ((line = fileIn.readLine()) != null) out.println(line);
            }
        } catch (IOException e) { e.printStackTrace(); }
    }
}

每個連接啟動一個新線程,雖然簡單但頻繁創建/銷毀線程有開銷,且無法限制最大併發數。

高性能:連接池服務端

預先創建固定數量的工作線程,使用共享隊列管理連接,避免線程頻繁創建,並能控制最大併發數:

public class PooledServer {
    private final List pool = Collections.synchronizedList(new LinkedList<>());
    private final int maxHandlers;

    public PooledServer(int maxHandlers) { this.maxHandlers = maxHandlers; }

    public void start() {
        for (int i = 0; i < maxHandlers; i++) {
            new Thread(new PooledHandler()).start();
        }
        // accept loop...
    }

    protected void handleConnection(Socket client) {
        synchronized (pool) {
            pool.add(client);
            pool.notifyAll();
        }
    }

    class PooledHandler implements Runnable {
        public void run() {
            while (true) {
                Socket client;
                synchronized (pool) {
                    while (pool.isEmpty()) {
                        try { pool.wait(); } catch (InterruptedException ignored) {}
                    }
                    client = pool.remove(0);
                }
                // process client...
            }
        }
    }
}

優勢:線程復用、可控併發、降低 GC 壓力。適用於高併發、長連接場景(如數據庫連接池、Web 服務器)。

總結

Java Socket 編程的核心在於理解客戶端/服務端模型、TCP 流特性及線程模型選擇。從基礎的阻塞 I/O 到多線程、連接池,每一步都在解決性能與擴展性的矛盾。實際項目中,建議優先考慮成熟的網絡框架(如 Netty),但掌握 Socket 基礎對排查問題、優化架構至關重要。

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