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

Java 網絡編程:Socket 客戶端/服務端完整實戰與 TCP 通信詳解

     編輯:Java 相關知識

Java 網絡編程基礎

Java 提供 java.net 封裝 TCP/UDP 協議,Socket 代表客戶端連接,ServerSocket 代表服務端監聽。本文聚焦 TCP 可靠流式通信。

客戶端核心流程


// 1. 建立連接(阻塞直到成功或超時)
Socket socket = new Socket();
socket.connect(new InetSocketAddress("host", port), 2000); // 2秒連接超時
socket.setSoTimeout(3000); // 3秒讀寫超時

// 2. 獲取流
InputStream in = socket.getInputStream();
OutputStream out = socket.getOutputStream();

// 3. 讀寫數據
Scanner scanner = new Scanner(in, "UTF-8");
PrintWriter writer = new PrintWriter(new OutputStreamWriter(out, "UTF-8"), true);

writer.println("Hello Server");
String response = scanner.nextLine();

// 4. 關閉
socket.close();

域名解析與 InetAddress


// 獲取本機 IP
InetAddress local = InetAddress.getLocalHost();
System.out.println(local.getHostAddress());

// 解析域名(支援負載均衡多 IP)
InetAddress[] addresses = InetAddress.getAllByName("google.com");
for (InetAddress addr : addresses) {
    System.out.println(addr.getHostAddress());
}

服務端核心架構:每連接一線程


public class TcpServer {
    private final int port;
    private final ExecutorService pool = Executors.newCachedThreadPool();
    
    public TcpServer(int port) { this.port = port; }
    
    public void start() throws IOException {
        try (ServerSocket server = new ServerSocket(port)) {
            server.setReuseAddress(true);
            System.out.println("Server listening on " + port);
            
            while (!Thread.currentThread().isInterrupted()) {
                Socket client = server.accept(); // 阻塞等待連接
                pool.execute(new ClientHandler(client));
            }
        }
    }
    
    private static class ClientHandler implements Runnable {
        private final Socket socket;
        private final int clientId;
        private static final AtomicInteger COUNTER = new AtomicInteger();
        
        ClientHandler(Socket socket) {
            this.socket = socket;
            this.clientId = COUNTER.incrementAndGet();
        }
        
        @Override
        public void run() {
            String clientInfo = socket.getInetAddress() + ":" + socket.getPort();
            System.out.println("Client #" + clientId + " connected: " + clientInfo);
            
            try (Scanner in = new Scanner(socket.getInputStream(), "UTF-8");
                 PrintWriter out = new PrintWriter(
                     new OutputStreamWriter(socket.getOutputStream(), "UTF-8"), true)) {
                
                out.println("Welcome! Type 'bye' to exit.");
                
                while (in.hasNextLine()) {
                    String msg = in.nextLine();
                    if ("bye".equalsIgnoreCase(msg)) {
                        out.println("Goodbye!");
                        break;
                    }
                    // 業務邏輯:回聲/處理
                    out.println("Echo: " + msg);
                }
            } catch (IOException e) {
                System.err.println("Client #" + clientId + " error: " + e.getMessage());
            } finally {
                try { socket.close(); } catch (IOException ignored) {}
                System.out.println("Client #" + clientId + " disconnected");
            }
        }
    }
    
    public static void main(String[] args) throws IOException {
        new TcpServer(8400).start();
    }
}

關鍵超時設置

⚠️ 無超時設置會導致線程永久阻塞,資源洩漏。

生產級增強建議

1. 線程池隔離


// IO 密集型用 CachedThreadPool 或自定義固定大小池
ExecutorService pool = new ThreadPoolExecutor(
    coreSize, maxSize, 60L, TimeUnit.SECONDS,
    new LinkedBlockingQueue<>(),
    new ThreadFactoryBuilder().setNameFormat("tcp-worker-%d").build(),
    new ThreadPoolExecutor.AbortPolicy()
);

2. 協議幀處理(粘包/拆包)


// 長度前綴協議
// 發送:[4字節長度][JSON內容]
// 接收:先讀 4 字節長度,再讀指定長度內容
DataOutputStream dos = new DataOutputStream(out);
byte[] jsonBytes = json.getBytes("UTF-8");
dos.writeInt(jsonBytes.length);
dos.write(jsonBytes);
dos.flush();

3. 優雅關閉


// 關閉流程
server.close(); // 拒絕新連接
pool.shutdown(); // 停止接受新任務
pool.awaitTermination(30, TimeUnit.SECONDS); // 等待進行中任務完成

4. NIO 非阻塞升級路徑

  • 高併發(C10K+)場景遷移到 Netty/Mina/Vert.x
  • 單機萬級連接:Selector + ByteBuffer + 零拷貝
  • 保持業務邏輯不變,只替換傳輸層

測試驗證


// 啟動服務端
java TcpServer

// 客戶端測試
telnet localhost 8400
# 或
nc localhost 8400
# 或 Java 客戶端代碼

總結

Socket 编程核心:連接建立 → 流獲取 → 協議編解碼 → 業務處理 → 異常處理 → 資源關閉。生產環境必須處理超時、線程池、協議幀、優雅關閉,高併發遷移 NIO 框架。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved
方法作用建議值
socket.connect(address, timeout)連接建立超時2-5 秒
socket.setSoTimeout(ms)讀操作阻塞超時3-30 秒
server.setSoTimeout(ms)accept() 超時定期檢查關閉標誌