架構圖
Client Server
│ │
├─ Socket("localhost",10000)─►
│ ├─ ServerSocket(10000).accept()
│ │ └─ new Thread(Handler).start()
│◄─ BufferedReader/PrintWriter ─┤
│ │
Client 端
import java.io.*;
import java.net.*;
public class SimpleClient {
public static void main(String[] args) throws Exception {
try (Socket socket = new Socket("localhost", 10000);
BufferedReader in = new BufferedReader(new InputStreamReader(socket.getInputStream()));
PrintWriter out = new PrintWriter(socket.getOutputStream(), true);
BufferedReader console = new BufferedReader(new InputStreamReader(System.in))) {
String line;
while ((line = console.readLine()) != null) {
out.println(line);
if ("bye".equalsIgnoreCase(line)) break;
System.out.println("Server: " + in.readLine());
}
}
}
}
Server 端 (多執行緒)
import java.io.*;
import java.net.*;
public class SimpleServer {
public static void main(String[] args) throws IOException {
try (ServerSocket server = new ServerSocket(10000)) {
System.out.println("Server started on 10000");
while (true) {
Socket client = server.accept();
new Thread(new ClientHandler(client)).start();
}
}
}
private static class ClientHandler implements Runnable {
private final Socket socket;
ClientHandler(Socket s) { this.socket = s; }
@Override public void run() {
try (BufferedReader in = new BufferedReader(new InputStreamReader(socket.getInputStream()));
PrintWriter out = new PrintWriter(socket.getOutputStream(), true)) {
String msg;
while ((msg = in.readLine()) != null) {
System.out.println("Received: " + msg);
out.println("Server echo: " + msg);
if ("bye".equalsIgnoreCase(msg)) break;
}
} catch (IOException e) {
e.printStackTrace();
} finally {
try { socket.close(); } catch (IOException ignored) {}
}
}
}
}
關鍵細節
PrintWriter(..., true):開啟 **auto-flush**,println 後自動刷新緩衝區。
- Server 使用 **一連線一執行緒** 模型,簡單但連線數大時會耗盡執行緒,生產環境建議用 **執行緒池** 或 **NIO (Netty)**。
try-with-resources 確保流與 Socket 正確關閉。
- 雙方約定以換行符
\n 作為訊息邊界,readLine() 即可讀取完整訊息。
進階擴充方向