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

Java Socket 通信實戰:從簡易 Echo 到多線程服務端完整演示

     編輯:Java 相關知識

Socket 是網絡編程的基石。本文提供兩個層次的完整示例:入門級單線程 Echo 模型與進階多線程生產級模型,涵蓋客戶端/服務端雙向代碼、關鍵 API 解析、常見坑點與擴展方向。

第一部分:極簡 Echo 模型(單線程、阻塞 IO)

服務端

public class EchoServer {
    public static void main(String[] args) throws IOException {
        try (ServerSocket server = new ServerSocket(60000)) {
            System.out.println("Server started on 60000");
            while (true) {
                Socket client = server.accept(); // 阻塞等待連接
                System.out.println("Client: " + client.getInetAddress());
                try (BufferedReader in = new BufferedReader(new InputStreamReader(client.getInputStream()));
                     PrintWriter out = new PrintWriter(client.getOutputStream(), true)) {
                    String line;
                    while ((line = in.readLine()) != null) {
                        System.out.println("Received: " + line);
                        out.println("--->" + line); // 回显
                    }
                }
            }
        }
    }
}

客戶端

public class EchoClient {
    public static void main(String[] args) throws IOException {
        try (Socket socket = new Socket("localhost", 60000);
             PrintWriter out = new PrintWriter(socket.getOutputStream(), true);
             BufferedReader in = new BufferedReader(new InputStreamReader(socket.getInputStream()));
             BufferedReader console = new BufferedReader(new InputStreamReader(System.in))) {

            String line;
            while ((line = console.readLine()) != null) {
                out.println(line);
                System.out.println("Server echo: " + in.readLine());
                if ("bye#".equals(line)) break;
            }
        }
    }
}

關鍵點

  • PrintWriter(..., true) 啟用自動刷新,每次 println 即發送。
  • 雙方約定換行符 作為消息邊界(readLine/println)。
  • 單線程模型一次只能服務一個客戶端,適合學習、測試。

第二部分:多線程生產級模型(每連接一線程)

服務端

public class ThreadedServer {
    static final int PORT = 12345;

    public static void main(String[] args) {
        try (ServerSocket server = new ServerSocket(PORT)) {
            System.out.println("Server started on " + PORT);
            while (true) {
                Socket client = server.accept();
                new Thread(new Handler(client)).start(); // 每連接啟動一線程
            }
        } catch (IOException e) { e.printStackTrace(); }
    }

    static class Handler implements Runnable {
        private final Socket socket;
        Handler(Socket socket) { this.socket = socket; }

        public void run() {
            try (DataInputStream in = new DataInputStream(socket.getInputStream());
                 DataOutputStream out = new DataOutputStream(socket.getOutputStream())) {

                String msg = in.readUTF(); // 必須配對 writeUTF
                System.out.println("Client: " + msg);

                // 模擬業務處理
                String reply = "OK: " + msg;
                out.writeUTF(reply);
            } catch (IOException e) {
                System.err.println("Handler error: " + e.getMessage());
            } finally {
                try { socket.close(); } catch (IOException ignored) {}
            }
        }
    }
}

客戶端

public class ThreadedClient {
    static final String HOST = "localhost";
    static final int PORT = 12345;

    public static void main(String[] args) throws IOException, InterruptedException {
        while (true) {
            try (Socket socket = new Socket(HOST, PORT);
                 DataOutputStream out = new DataOutputStream(socket.getOutputStream());
                 DataInputStream in = new DataInputStream(socket.getInputStream());
                 BufferedReader console = new BufferedReader(new InputStreamReader(System.in))) {

                System.out.print("Send: ");
                String send = console.readLine();
                out.writeUTF(send);

                String reply = in.readUTF();
                System.out.println("Reply: " + reply);

                if ("OK".equals(reply)) {
                    System.out.println("Closing...");
                    Thread.sleep(500);
                    break;
                }
            }
        }
    }
}

核心 API 對照與避坑指南

場景推薦流注意事項
文本行協議BufferedReader/PrintWriter + readLine/println自動處理換行,跨平台兼容
二進制/定長協議DataInputStream/DataOutputStream + readUTF/writeUTF雙端必須配對,否則 EOFException
高性能/大數據NIO (Selector/Channel) 或 Netty非阻塞、零拷貝、可擴展至萬級連接

常見錯誤 Top 3

  1. 流包裝順序錯:new DataInputStream(new BufferedInputStream(socket.getInputStream())) — 先緩衍再數據流。
  2. 忘記關閉資源:導致端口佔用、文件句柄洩漏。Java 7+ 請用 try-with-resources。
  3. readUTF/writeUTF 不匹配:一方用 readLine 另一方用 writeUTF → 直接拋 EOFException。

架構演進路線圖

單線程阻塞 IO (Demo)
      ↓
多線程阻塞 IO (本文 ThreadedServer) — 適合百級併發
      ↓
線程池 + 阻塞 IO — 控制線程數,避免 OOM
      ↓
NIO Selector (非阻塞) — 千級併發,單線程輪詢
      ↓
Netty / Mina (成熟 NIO 框架) — 生產首選,解決黏包/半包、內存管理、編解碼

掌握 Socket 基礎,是理解 HTTP、RPC、微服務通信的前提。建議先跑通本文兩個 Demo,再嘗試加入心跳檢測、長度頭解碼器、優雅關閉等工程化細節。

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