項目結構
Server.java:監聽端口、多線程處理連接、文本反轉服務。
Client.java:連接服務器、控制台交互、收發文本。
Server.java 完整代碼
import java.io.*;
import java.net.*;
public class Server extends Thread {
public final static int DEFAULT_PORT = 6543;
protected int port;
protected ServerSocket listenSocket;
// 統一異常處理:打印並退出
public static void fail(Exception e, String msg) {
System.err.println(msg + ": " + e);
System.exit(1);
}
// 構造器:綁定端口、啟動監聽線程
public Server(int port) {
if (port == 0) port = DEFAULT_PORT;
this.port = port;
try {
listenSocket = new ServerSocket(port);
} catch (IOException e) {
fail(e, "Exception creating server socket");
}
System.out.println("Server: listening on port " + port);
this.start(); // 啟動 run() 監聽循環
}
// 監聽線程主體:無限循環 accept,每連接創建 Connection 線程
public void run() {
try {
while (true) {
Socket clientSocket = listenSocket.accept();
new Connection(clientSocket); // Connection 構造器內啟動線程
}
} catch (IOException e) {
fail(e, "Exception while listening for connections");
}
}
// 主方法:解析參數啟動服務器
public static void main(String[] args) {
int port = 0;
if (args.length == 1) {
try { port = Integer.parseInt(args[0]); }
catch (NumberFormatException e) { port = 0; }
}
new Server(port);
}
}
// 連接處理線程:負責單個客戶端的收發邏輯
class Connection extends Thread {
protected Socket client;
protected DataInputStream in;
protected PrintStream out;
public Connection(Socket clientSocket) {
this.client = clientSocket;
try {
in = new DataInputStream(client.getInputStream());
out = new PrintStream(client.getOutputStream());
} catch (IOException e) {
try { client.close(); } catch (IOException ignored) {}
System.err.println("Exception while getting socket stream: " + e);
return;
}
this.start(); // 啟動 run() 處理循環
}
public void run() {
try {
while (true) {
String line = in.readLine(); // 讀取一行文本
if (line == null) break; // 客戶端關閉
// 反轉文本
StringBuffer rev = new StringBuffer(line.length());
for (int i = line.length() - 1; i >= 0; i--) {
rev.append(line.charAt(i));
}
out.println(rev.toString()); // 發送反轉結果
}
} catch (IOException ignored) {
} finally {
try { client.close(); } catch (IOException ignored) {}
}
}
}
Client.java 完整代碼
import java.io.*;
import java.net.*;
public class Client {
public static final int DEFAULT_PORT = 6543;
public static void usage() {
System.out.println("Usage: java Client [port]");
System.exit(0);
}
public static void main(String[] args) {
int port = DEFAULT_PORT;
Socket socket = null;
// 參數解析
if (args.length != 1 && args.length != 2) usage();
if (args.length == 2) {
try { port = Integer.parseInt(args[1]); }
catch (NumberFormatException e) { usage(); }
}
try {
// 建立連接
socket = new Socket(args[0], port);
DataInputStream in = new DataInputStream(socket.getInputStream());
PrintStream out = new PrintStream(socket.getOutputStream());
DataInputStream console = new DataInputStream(System.in);
System.out.println("Connected to " + socket.getInetAddress() + ":" + socket.getPort());
// 交互循環
while (true) {
System.out.print(" > ");
System.out.flush();
String line = console.readLine(); // 讀取控制台輸入
if (line == null) break; // EOF (Ctrl+D / Ctrl+Z)
out.println(line); // 發送給服務器
String response = in.readLine(); // 接收服務器響應
if (response == null) {
System.out.println("Connection closed by server.");
break;
}
System.out.println(response); // 顯示響應
}
} catch (IOException e) {
System.err.println(e);
} finally {
if (socket != null) {
try { socket.close(); } catch (IOException ignored) {}
}
}
}
}
運行步驟
- 編譯:
javac Server.java Client.java
- 啟動服務端:
java Server 6543(端口可省略,默認 6543)
- 啟動客戶端:
java Client localhost 6543
- 客戶端控制台輸入文本,服務端返回反轉文本。輸入 EOF (Ctrl+D / Ctrl+Z) 退出。
核心設計點解析
| 點 | 說明 |
| ServerSocket | 服務端專用,accept() 阻塞等待連接,返回新 Socket 處理具體通信。 |
| 多線程模型 | 主線程 Server.run() 循環 accept;每連接一個 Connection 線程隔離 IO,互不阻塞。 |
| 流選擇 | DataInputStream.readLine() / PrintStream.println() 按行處理文本,適配控制台交互。 |
| 資源釋放 | finally 確保 socket.close(),防止句柄洩漏。 |
| 異常處理 | 統一 fail() 打印並退出,簡化邏輯;生產環境應改為日誌+重試/熔斷。 |
擴展方向
- 協議升級:定長/分隔符/長度前綴/Protobuf/JSON,替換
readLine。
- NIO/Netty:
Selector 非阻塞 IO,支撐萬級連接(C10K 問題)。
- 心跳/重連/超時/SSL/TLS 安全傳輸。
- 線程池:
ExecutorService 替換 new Thread(),控制併發數。
總結
本文提供了基於 ServerSocket/Socket + 多線程的經典 Java 同步阻塞 IO 服務端/客戶端完整實現。代碼結構清晰、邏輯最小化,是學習 Java 網絡編程、理解 BIO 模型、進階 NIO/Netty 的最佳起點。
- 上一篇文章: Java 類加載器深度解析:雙親委派、自定義加載、Web 容器隔離與隱式顯式加載
- 下一篇文章: Java 網絡編程基礎(一):C/S 與 B/S 架構、網絡類庫、通信流程詳解
Copyright ©
程式碼之家 All Rights Reserved