網路程式設計本質是兩個行程間的資料交換。Client/Server 模型中,Server 提供固定位址(IP + Port)監聽,Client 主動發起連線。Java java.net 套件封裝底層 Socket 細節,提供 ServerSocket、Socket、InetAddress 等核心類別,搭配 java.io 串流完成資料收發。
// Server
ServerSocket server = new ServerSocket(5678);
Socket client = server.accept(); // 阻塞等待
BufferedReader in = new BufferedReader(new InputStreamReader(client.getInputStream()));
PrintWriter out = new PrintWriter(client.getOutputStream(), true); // auto-flush
String line;
while ((line = in.readLine()) != null) {
System.out.println("Received: " + line);
out.println("Echo: " + line);
if ("end".equalsIgnoreCase(line)) break;
}
client.close();
// Client
Socket server = new Socket(InetAddress.getLocalHost(), 5678);
BufferedReader in = new BufferedReader(new InputStreamReader(server.getInputStream()));
PrintWriter out = new PrintWriter(server.getOutputStream(), true);
BufferedReader keyboard = new BufferedReader(new InputStreamReader(System.in));
String input;
while ((input = keyboard.readLine()) != null) {
out.println(input);
System.out.println("Server: " + in.readLine());
if ("end".equalsIgnoreCase(input)) break;
}
server.close();
在 accept() 外層加入 while(true) 迴圈,每次連線建立新 Socket 處理。缺點:同一時間僅能服務一個客戶端,其他需排隊。
public class MultiUserServer extends Thread {
private Socket client;
public MultiUserServer(Socket client) {
this.client = client;
}
public void run() {
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("Thread-" + getId() + ": " + line);
out.println("Received by " + getName());
if ("end".equalsIgnoreCase(line)) break;
}
} catch (IOException e) {
e.printStackTrace();
} finally {
try { client.close(); } catch (IOException ignored) {}
}
}
public static void main(String[] args) throws IOException {
ServerSocket server = new ServerSocket(5678);
System.out.println("Multi-thread server started on 5678");
while (true) {
new MultiUserServer(server.accept()).start(); // 每連線一執行緒
}
}
}
| 階段 | 核心類別 | 關鍵方法 | 適用場景 |
|---|---|---|---|
| 基礎通訊 | Socket, ServerSocket | accept(), getInputStream(), getOutputStream() | 單一長連線、教學驗證 |
| 多客戶排隊 | ServerSocket + 迴圈 | while(true) accept() | 低併發、請求處理極快 |
| 多線程併發 | Thread/Runnable + Socket | start(), run() | 一般業務、中等併發(<500) |
| 執行緒池 | ExecutorService | submit(), execute() | 高併發、資源可控、生產環境 |
| NIO 非阻塞 | Selector, SocketChannel | select(), register() | 超高併發(萬級)、長連線 |
StandardCharsets.UTF_8 避免中文亂碼socket.setSoTimeout(ms)、server.setSoTimeout(ms) 防止永久阻塞PrintWriter 自動刷新:建構子 new PrintWriter(out, true) 啟用 auto-flush,否則需手動 flush()。
BufferedReader.readLine() 回傳 null:表示對端關閉連線(EOF),非空字串。
InetAddress.getLocalHost() 陷阱:某些環境回傳 127.0.0.1,Client 需指定實際 Server IP。
埠號衝突:1024 以下需權限,建議使用 1024-65535 且避開常用服務埠。