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
| 特性 | TCP | UDP |
|---|---|---|
| 連線 | 需要建立連線 | 無連線 |
| 可靠性 | 可靠,保證送達 | 不可靠,可能丟失 |
| 順序 | 保證順序 | 不保證順序 |
| 速度 | 較慢 | 較快 |
| 用途 | HTTP, FTP, Email | DNS, 串流, 遊戲 |
重點整理
- TCP:使用
Socket和ServerSocket,可靠有序 - UDP:使用
DatagramSocket和DatagramPacket,快速但不可靠 - 伺服器應使用執行緒池處理多客戶端
- 設定適當的超時避免永久阻塞
- 使用 try-with-resources 自動關閉 Socket
- 生產環境建議使用 Netty 等框架