ServerSocket 是 Java BIO 模型下服務端監聽連接的核心類。掌握其構造器、常用方法、Socket 選項配置,是構建高性能、穩定網絡服務的基礎。本文系統梳理四種構造器差異、核心 API、四大關鍵 Socket 選項及最佳實踐。
| 構造器 | 參數含義 | 適用場景 | 異常 |
|---|---|---|---|
ServerSocket() | 無參,創建未綁定 Socket | 需先設置選項(如 reuseAddress)再綁定端口 | IOException |
ServerSocket(int port) | 綁定指定端口,backlog 默認 50 | 快速啟動,單實例服務 | BindException (端口占用) |
ServerSocket(int port, int backlog) | 指定端口 + 接受隊列長度 | 高併發連接突發場景,調大 backlog | BindException |
ServerSocket(int port, int backlog, InetAddress bindAddr) | 指定端口、backlog、綁定 IP | 多網卡服務器,綁定內網/外網特定 IP | BindException |
backlog 參數深度:內核維護兩個隊列:
1. 未完成連接隊列(SYN_RCVD 狀態)
2. 已完成連接隊列(ESTABLISHED,等待accept()取走)
backlog歷史上指已完成隊列大小,Linux 2.2+ 為兩隊列之和。高併發建議設置 1024 或更大,並同步調內核參數net.core.somaxconn。
public InetAddress getInetAddress() // 返回綁定的本地 IP(多網卡時確認生效 IP)
public int getLocalPort() // 返回綁定端口(0 表示系統自動分配時獲取實際端口)
public void setSoTimeout(int timeout) // 毫秒,0 表示永不超時
public int getSoTimeout()
accept() 阻塞等待時間。SocketTimeoutException,可用於實現優雅關閉輪詢:while(!shutdown) { try { socket = server.accept(); } catch (SocketTimeoutException e) { /* 檢查 shutdown 標誌 */ } }public void setReuseAddress(boolean on)
public boolean getReuseAddress()
TIME_WAIT 狀態(默認 60s),開啟此選項允許立即重綁定。bind() 前調用,否則無效。SO_REUSEADDR,不同於 SO_REUSEPORT(Linux 3.9+,多線程負載均衡)。public void setReceiveBufferSize(int size) // 字節
public int getReceiveBufferSize()
net.core.rmem_default 決定(通常 128KB~256KB)。net.core.rmem_max 限制,調用 getReceiveBufferSize() 可查看生效值。public void setPerformancePreferences(int connectionTime, int latency, int bandwidth)
public class ServerBootstrap {
public static void main(String[] args) throws IOException {
int port = 8080;
ServerSocket server = new ServerSocket(); // 1. 未綁定
// 2. 設置選項(bind 前)
server.setReuseAddress(true);
server.setReceiveBufferSize(256 * 1024); // 256KB
server.setSoTimeout(3000); // 3s 輪詢關閉標誌
// 3. 綁定端口
server.bind(new InetSocketAddress(port), 1024); // backlog=1024
System.out.println("Server listening on " + server.getLocalPort());
// 4. 接受循環
volatile boolean running = true;
Runtime.getRuntime().addShutdownHook(new Thread(() -> running = false));
try {
while (running) {
try {
Socket client = server.accept();
// 提交線程池處理,避免阻塞 accept
executor.submit(() -> handle(client));
} catch (SocketTimeoutException ignored) {
// 定期檢查 running 標誌
}
}
} finally {
server.close();
executor.shutdown();
}
}
}
ServerSocket 屬於阻塞 IO(BIO),每連接一線程,C10K 問題明顯。生產環境高併發建議遷移至:
ServerSocketChannel + Selector,單線程多路複用。