Java Socket 網路程式設計

Socket 是網路通訊的基礎,Java 提供 java.net 套件進行 TCP 和 UDP 通訊。

TCP Socket

伺服器端

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

public class TcpServer {
    public static void main(String[] args) throws IOException {
        int port = 8080;
        
        try (ServerSocket serverSocket = new ServerSocket(port)) {
            System.out.println("伺服器啟動,監聽埠口 " + port);
            
            while (true) {
                Socket clientSocket = serverSocket.accept();
                System.out.println("客戶端連線: " + clientSocket.getInetAddress());
                
                // 處理客戶端(這裡用單執行緒,實際應使用執行緒池)
                handleClient(clientSocket);
            }
        }
    }
    
    private static void handleClient(Socket socket) {
        try (
            BufferedReader in = new BufferedReader(
                new InputStreamReader(socket.getInputStream()));
            PrintWriter out = new PrintWriter(socket.getOutputStream(), true)
        ) {
            String line;
            while ((line = in.readLine()) != null) {
                System.out.println("收到: " + line);
                out.println("Echo: " + line);
                
                if ("bye".equalsIgnoreCase(line)) break;
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

客戶端

public class TcpClient {
    public static void main(String[] args) throws IOException {
        String host = "localhost";
        int port = 8080;
        
        try (
            Socket socket = new Socket(host, port);
            BufferedReader in = new BufferedReader(
                new InputStreamReader(socket.getInputStream()));
            PrintWriter out = new PrintWriter(socket.getOutputStream(), true);
            BufferedReader userInput = new BufferedReader(
                new InputStreamReader(System.in))
        ) {
            System.out.println("已連線到伺服器");
            
            String line;
            while ((line = userInput.readLine()) != null) {
                out.println(line);
                System.out.println(in.readLine());
                
                if ("bye".equalsIgnoreCase(line)) break;
            }
        }
    }
}

多執行緒伺服器

public class MultiThreadServer {
    public static void main(String[] args) throws IOException {
        ExecutorService executor = Executors.newFixedThreadPool(10);
        
        try (ServerSocket serverSocket = new ServerSocket(8080)) {
            while (true) {
                Socket clientSocket = serverSocket.accept();
                executor.submit(() -> handleClient(clientSocket));
            }
        }
    }
    
    private static void handleClient(Socket socket) {
        try (socket;
             BufferedReader in = new BufferedReader(
                 new InputStreamReader(socket.getInputStream()));
             PrintWriter out = new PrintWriter(socket.getOutputStream(), true)
        ) {
            String line;
            while ((line = in.readLine()) != null) {
                out.println("Echo: " + line);
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

UDP Socket

伺服器端

public class UdpServer {
    public static void main(String[] args) throws IOException {
        int port = 9090;
        byte[] buffer = new byte[1024];
        
        try (DatagramSocket socket = new DatagramSocket(port)) {
            System.out.println("UDP 伺服器啟動,埠口 " + port);
            
            while (true) {
                DatagramPacket request = new DatagramPacket(buffer, buffer.length);
                socket.receive(request);
                
                String message = new String(request.getData(), 0, request.getLength());
                System.out.println("收到: " + message);
                
                // 回應
                String response = "Echo: " + message;
                DatagramPacket reply = new DatagramPacket(
                    response.getBytes(),
                    response.length(),
                    request.getAddress(),
                    request.getPort()
                );
                socket.send(reply);
            }
        }
    }
}

客戶端

public class UdpClient {
    public static void main(String[] args) throws IOException {
        String host = "localhost";
        int port = 9090;
        
        try (DatagramSocket socket = new DatagramSocket()) {
            InetAddress address = InetAddress.getByName(host);
            
            // 發送
            String message = "Hello UDP";
            DatagramPacket request = new DatagramPacket(
                message.getBytes(),
                message.length(),
                address,
                port
            );
            socket.send(request);
            
            // 接收
            byte[] buffer = new byte[1024];
            DatagramPacket response = new DatagramPacket(buffer, buffer.length);
            socket.receive(response);
            
            String reply = new String(response.getData(), 0, response.getLength());
            System.out.println("回應: " + reply);
        }
    }
}

Socket 選項

Socket socket = new Socket();

// 設定超時
socket.setSoTimeout(5000);       // 讀取超時(毫秒)
socket.connect(address, 3000);   // 連線超時

// 其他選項
socket.setKeepAlive(true);       // 保持連線
socket.setTcpNoDelay(true);      // 禁用 Nagle 演算法
socket.setReuseAddress(true);    // 允許重用位址
socket.setSendBufferSize(8192);  // 發送緩衝區
socket.setReceiveBufferSize(8192); // 接收緩衝區

簡單 HTTP 伺服器

public class SimpleHttpServer {
    public static void main(String[] args) throws IOException {
        try (ServerSocket serverSocket = new ServerSocket(8080)) {
            System.out.println("HTTP 伺服器啟動: http://localhost:8080");
            
            while (true) {
                Socket client = serverSocket.accept();
                handleHttp(client);
            }
        }
    }
    
    private static void handleHttp(Socket socket) {
        try (socket;
             BufferedReader in = new BufferedReader(
                 new InputStreamReader(socket.getInputStream()));
             OutputStream out = socket.getOutputStream()
        ) {
            // 讀取請求
            String requestLine = in.readLine();
            System.out.println("請求: " + requestLine);
            
            // 跳過標頭
            String line;
            while ((line = in.readLine()) != null && !line.isEmpty()) {}
            
            // 回應
            String body = "<html><body><h1>Hello from Java!</h1></body></html>";
            String response = "HTTP/1.1 200 OK\r\n" +
                "Content-Type: text/html\r\n" +
                "Content-Length: " + body.length() + "\r\n" +
                "\r\n" +
                body;
            
            out.write(response.getBytes());
            out.flush();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

TCP vs UDP

特性TCPUDP
連線需要建立連線無連線
可靠性可靠,保證送達不可靠,可能丟失
順序保證順序不保證順序
速度較慢較快
用途HTTP, FTP, EmailDNS, 串流, 遊戲

重點整理

  • TCP:使用 SocketServerSocket,可靠有序
  • UDP:使用 DatagramSocketDatagramPacket,快速但不可靠
  • 伺服器應使用執行緒池處理多客戶端
  • 設定適當的超時避免永久阻塞
  • 使用 try-with-resources 自動關閉 Socket
  • 生產環境建議使用 Netty 等框架