Socket 是網路程式設計核心抽象。Java 提供 java.net.Socket (TCP) 與 DatagramSocket (UDP) 兩套 API。本文對比兩大協定特性,提供完整客戶端/伺服端代碼範例,並指導多執行緒併發擴展與 GUI 整合方向。
| 特性 | TCP (Socket) | UDP (DatagramSocket) |
|---|---|---|
| 協定全名 | Transmission Control Protocol | User Datagram Protocol |
| 連線模式 | 面向連線 (三次交握) | 無連線 |
| 可靠性 | 保證送達、有序、無重複 | 不保證送達、順序、可能重複 |
| 流量控制 | 滑動視窗、阻塞控制 | 無 |
| 資料界限 | 位元組流 (無訊息邊界) | 資料報 (保留訊息邊界) |
| 開銷 | 較大 (標頭 20+ bytes) | 較小 (標頭 8 bytes) |
| 適用場景 | 檔案傳輸、Web、郵件、資料庫 | 串流媒體、即時遊戲、DNS、SNMP |
| Java 核心類 | Socket, ServerSocket | DatagramSocket, DatagramPacket |
public class TcpEchoServer {
public static void main(String[] args) throws IOException {
try (ServerSocket server = new ServerSocket(2000)) {
System.out.println("TCP Server listening on port 2000...");
while (true) {
try (Socket client = server.accept();
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("Echo: " + line); // 回傳
}
} catch (IOException e) {
System.err.println("Client error: " + e.getMessage());
}
}
}
}
}
public class TcpEchoClient {
public static void main(String[] args) throws IOException {
try (Socket socket = new Socket("localhost", 2000);
PrintWriter out = new PrintWriter(socket.getOutputStream(), true);
BufferedReader in = new BufferedReader(new InputStreamReader(socket.getInputStream()));
BufferedReader stdIn = new BufferedReader(new InputStreamReader(System.in))) {
String userInput;
while ((userInput = stdIn.readLine()) != null) {
out.println(userInput);
System.out.println("Server: " + in.readLine());
}
}
}
}
public class UdpSender {
public static void main(String[] args) throws IOException {
if (args.length < 3) {
System.err.println("Usage: java UdpSender [-f file]");
return;
}
InetAddress address = InetAddress.getByName(args[0]);
int port = Integer.parseInt(args[1]);
byte[] data;
if ("-f".equals(args[2])) {
// 讀取檔案
Path path = Paths.get(args[3]);
data = Files.readAllBytes(path);
} else {
// 合併剩餘參數為訊息
String msg = String.join(" ", Arrays.copyOfRange(args, 2, args.length));
data = msg.getBytes(StandardCharsets.UTF_8);
}
try (DatagramSocket socket = new DatagramSocket()) {
DatagramPacket packet = new DatagramPacket(data, data.length, address, port);
socket.send(packet);
System.out.println("Sent " + data.length + " bytes to " + address + ":" + port);
}
}
}
public class UdpReceiver {
public static void main(String[] args) throws IOException {
if (args.length != 1) {
System.err.println("Usage: java UdpReceiver ");
return;
}
int port = Integer.parseInt(args[0]);
byte[] buffer = new byte[2048];
try (DatagramSocket socket = new DatagramSocket(port)) {
System.out.println("UDP Receiver listening on port " + port);
while (true) {
DatagramPacket packet = new DatagramPacket(buffer, buffer.length);
socket.receive(packet); // 阻塞等待
String msg = new String(packet.getData(), 0, packet.getLength(), StandardCharsets.UTF_8);
System.out.println(packet.getAddress().getHostAddress() + ": " + msg);
// 重設長度供下次複用 (Java 1.1+ 必須)
packet.setLength(buffer.length);
}
}
}
}
buffer.length 限制單次接收上限,超截資料將丟失。大檔案需分片或改用 TCPDatagramPacket.setLength():複用 Packet 前必須重設長度,否則後續接收會被截斷readLine() 依賴換行符;二進位協議需自定義長度前綴或分隔符try-with-resources 確保 Socket/Stream 正確關閉
public class ConcurrentTcpServer {
private static final ExecutorService pool = Executors.newCachedThreadPool();
public static void main(String[] args) throws IOException {
try (ServerSocket server = new ServerSocket(2000)) {
System.out.println("Concurrent TCP Server started...");
while (true) {
Socket client = server.accept();
pool.submit(() -> handleClient(client));
}
} finally {
pool.shutdown();
}
}
private static void handleClient(Socket client) {
try (BufferedReader in = new BufferedReader(new InputStreamReader(client.getInputStream()));
PrintWriter out = new PrintWriter(client.getOutputStream(), true)) {
String line;
while ((line = in.readLine()) != null) {
out.println("Thread-" + Thread.currentThread().getId() + ": " + line);
}
} catch (IOException e) {
System.err.println("Client handler error: " + e.getMessage());
}
}
}
JTextArea 顯示訊息、JTextField 輸入、背景執行緒接收伺服端推送Set<PrintWriter> 廣播訊息給所有連線客戶端SwingUtilities.invokeLater() 更新 UIsocket.setSoTimeout()SSLSocketFactory / SSLServerSocketFactory掌握 TCP/UDP Socket 基礎,是理解 HTTP、RPC、微服務通訊底層原理的關鍵。從裸 Socket 起步,逐步引入成熟框架,能建立扎實的網路程式設計功力。