首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> Java 程式設計 >> Java 相關知識

Java 網路程式設計精通指南(18):Socket 選項進階 — getter/setter 方法詳解(二)

     編輯:Java 相關知識

接續上一篇,本文深入講解 Socket 類中用於獲取與設置 Socket 選項的 8 對 getter/setter 方法。這些選項定義於 java.net.SocketOptions 介面,直接影響數據傳輸行為、性能與可靠性。

Socket 選項速覽表

常量名值對應方法用途
TCP_NODELAY0x0001getTcpNoDelay / setTcpNoDelay控制 Nagle 算法開關
SO_REUSEADDR0x0004getReuseAddress / setReuseAddress允許端口重用
SO_LINGER0x0080getSoLinger / setSoLinger控制 close() 延遲關閉行為
SO_TIMEOUT0x1006getSoTimeout / setSoTimeout設置讀取超時(毫秒)
SO_SNDBUF0x1001getSendBufferSize / setSendBufferSize發送緩衝區大小
SO_RCVBUF0x1002getReceiveBufferSize / setReceiveBufferSize接收緩衝區大小
SO_KEEPALIVE0x0008getKeepAlive / setKeepAlive啟用心跳保活機制
SO_OOBINLINE0x1003getOOBInline / setOOBInline帶外數據內聯接收

核心選項深度解析

1. TCP_NODELAY — 關閉 Nagle 算法

預設開啟。Nagle 算法會將小數據包合併發送,減少網路擁塞,但會引入延遲。對於即時性要求高的場景(遊戲、Telnet、金融交易),需關閉:

socket.setTcpNoDelay(true); // 禁用 Nagle,每次 write 立即發送

獲取狀態:boolean on = socket.getTcpNoDelay();

2. SO_REUSEADDR — 端口重用

允許多個 Socket 綁定同一端口,解決 Address already in use: JVM_Bind 問題。必須在 bind() 前調用,且所有共用端口的 Socket 皆需啟用:

Socket s1 = new Socket();
s1.setReuseAddress(true);
s1.bind(new InetSocketAddress("127.0.0.1", 88));

Socket s2 = new Socket();
s2.setReuseAddress(true);
s2.bind(new InetSocketAddress("127.0.0.1", 88)); // 成功

注意:構造方法綁定端口時無法設置此選項,必須用無參構造 + bind()。

3. SO_LINGER — 延遲關閉控制

影響 close() 行為:

  • setSoLinger(true, n):關閉時最多阻塞 n 秒(0~65535),嘗試發送未完成數據;超時則丟棄數據並返回。
  • setSoLinger(false, 0) 或 setSoLinger(true, 0):立即返回,未發送數據丟棄(預設行為)。
if (socket.getSoLinger() == -1) { // -1 表示未啟用
    socket.setSoLinger(true, 60); // 設置 1 分鐘延遲關閉
}

底層不支持時拋 SocketException;linger 為負數拋 IllegalArgumentException。

4. SO_TIMEOUT — 讀取超時

setSoTimeout(ms) 設置 read() 最大阻塞時間。0 表示無限等待(預設)。超時拋 InterruptedIOException(即 SocketTimeoutException),流保持開啟可重試:

socket.setSoTimeout(30_000); // 30 秒讀取超時

負數拋 IllegalArgumentException;底層不支持拋 SocketException。

5. SO_SNDBUF / SO_RCVBUF — 緩衝區調優

預設 8KB。過小會導致頻繁系統調用降低吞吐;過大可能造成內存壓力。建議根據網路帶寬延遲積(BDP)調整:

socket.setSendBufferSize(64 * 1024); // 64KB 發送緩衝
socket.setReceiveBufferSize(64 * 1024); // 64KB 接收緩衝

size 必須為正整數,否則拋 IllegalArgumentException。

6. SO_KEEPALIVE — 保活機制

啟用後,空閒約 2 小時發送探測包;若 11 分鐘無響應再發一次;12 分鐘仍無響應則關閉連接。適用於長連接檢測對端存活:

socket.setKeepAlive(true); // 啟用保活

7. SO_OOBINLINE — 帶外數據內聯

啟用後,可用 sendUrgentData(int) 發送單字節緊急數據(繞過發送緩衝區立即發送)。接收端需同樣啟用此選項,否則無法接收。接收時帶外數據與普通數據混合在輸入流中,無法區分來源:

// 發送端
socket.setOOBInline(true);
socket.sendUrgentData(65); // 發送 'A' (低字節)

// 接收端
serverSocket.setOOBInline(true);
InputStream in = socket.getInputStream();
// 讀取時 'A' 會夾雜在普通數據流中

sendUrgentData 僅發送 int 低 8 位;高 24 位被忽略。

完整示例:帶外數據通訊

// Server.java
public class Server {
    public static void main(String[] args) throws Exception {
        ServerSocket server = new ServerSocket(1234);
        System.out.println("Server started on 1234");
        while (true) {
            Socket s = server.accept();
            s.setOOBInline(true);
            BufferedReader br = new BufferedReader(new InputStreamReader(s.getInputStream()));
            System.out.println(br.readLine()); // 讀取兩行
            System.out.println(br.readLine());
            s.close();
        }
    }
}

// Client.java
public class Client {
    public static void main(String[] args) throws Exception {
        Socket s = new Socket("127.0.0.1", 1234);
        s.setOOBInline(true);
        BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(s.getOutputStream()));
        bw.write('C'); bw.write("hello world\r\n"); bw.flush();
        s.sendUrgentData(65);  // 'A'
        s.sendUrgentData(322); // 低字節 66 -> 'B'
        bw.flush();
        s.sendUrgentData(214); s.sendUrgentData(208); // "中" 兩字節
        s.sendUrgentData(185); s.sendUrgentData(250); // "國" 兩字節
        s.close();
    }
}

運行結果顯示帶外數據優先到達:ABChello world\n中國。驗證了 sendUrgentData 立即發送特性,而普通 write 需 flush()。

關鍵提醒

  • 所有 setter 方法均可能拋 SocketException(底層不支持)或 IllegalArgumentException(參數非法)。
  • SO_REUSEADDR、SO_LINGER 必須在 bind()/connect() 前設置。
  • SO_OOBINLINE 需雙端同時啟用。
  • 緩衝區大小調整需測試驗證,盲目增大可能適得其反。
  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved