亚洲在线久爱草,狠狠天天香蕉网,天天搞日日干久草,伊人亚洲日本欧美

設置 Java Socket 選項

1. 前言

前面章節介紹了 Java TCP、UDP Socket 編程方法,按照文中介紹的方法去編寫 Socket 程序,是完全可以正常工作的。其實,TCP/IP 協議棧允許你對 Socket 做一些定制,比如設置 Socket 的接收、發送緩沖區的大小,這就是常說的 Socket 選項。

本文首先會以 Linux 系統為例,介紹操作系統 Socket 選項的基本概念,然后再介紹 Java 中如何去設置 Socket 選項。

2. Socket 選項的概念

操作系統協議棧支持的 Socket 選項參數有很多,匯總起來如下圖所示:

圖片描述

從圖中可以看出,Socket 選項按照級別進行分類,級別有很多種,但是總結起來分兩類:

  • 通用 Socket 級別的選項。枚舉值為 SOL_SOCKET。
  • 協議相關的選項。協議棧為我們提供了控制所有協議的選項,比如 IP、IPv6、TCP、UDP、ICMP 等。枚舉值的格式為 IPPROTO_XXX,XXX 代表協議。

每一種選項級別下面包含了很多選項參數。比如,通用 Socket 選項的級別枚舉值是 SOL_SOCKET,其下面包含 SO_RCVBUF 和 SO_SNDBUF 選項參數;IP 協議選項的級別的枚舉值是 IPPROTO_IP,其下面包含 IP_TTL、IP_TOS 等選項參數。

在 Linux 系統下,所有的選項參數都可以在幫助手冊里面查找,具體方法如下:

通用 Socket 級別選項參數

man 7 socket

IP 協議級別選項參數:

man 7 ip

IPv6 協議級別選項參數:

man 7 ipv6

TCP 協議級別選項參數:

man 7 tcp

UDP 協議級別選項參數:

man 7 udp

Socket 選項參數最終是如何設置到協議棧的呢?協議棧提供了 getsockopt() 和 setsockopt() 兩個 C 語言函數,分別用于獲取和設置選項參數。

調用兩個函數所需要包含的頭文件,以及他們的聲明如下:

#include <sys/types.h> 
#include <sys/socket.h>

int getsockopt(int sockfd, int level, int optname, void *optval, socklen_t *optlen);
int setsockopt(int sockfd, int level, int optname, const void *optval, socklen_t optlen);

如果你對系統本身的 Socket 選項感興趣,可以通過 man 查找相關幫助。本節重點介紹通用 Socket 選項。

3. 通用 Socket 選項

通用 Socket 選項的 level 枚舉值是 SOL_SOCKET。
表格中選項名稱不用多說,數據類型列表示選項值的類型,大多數是整形,還有一些是結構體類型。有的選項是既可以設置值也可以讀取值,用 set 表示;有的選項只能讀取值,用 get 表示。常見選項參數如下:

選項名稱 數據類型 get 或 set 說明
SO_BROADCAST int set 設置 Socket 可以進行局域網廣播,目標 IP 需要填網段的廣播地址或者是統一受限廣播地址 255.255.255.255。
SO_KEEPALIVE int set 用于設置 TCP 連接的?;睿话愫苌儆?。
SO_LINGER struct linger set 用于設置當 TCP 連接已經關閉,但是未發送數據等待時間。通常設置 SO_LINGER 等待時間為 0,解決大量 TIME_WAIT 狀態的問題。
SO_OOBINLINE int set 用于設置將“帶外數據”作為普通數據流來處理。
SO_RCVBUF int set 設置 Socket 接收緩沖區大小。
SO_REUSEADDR int set 用于設置在調用 bind() 函數時,重用已經 bind 的 Socket 地址。
SO_SNDBUF int set 設置 Socket 發送緩沖區大小。

4. 常用選項說明

下來,我們對 Socket 編程中常用的 Socket 選項重點介紹。

4.1 SO_REUSEADDR

TCP 連接關閉過程中,主動關閉的一方會處于 TIME_WAIT 狀態,要等待 2MSL 時間。而服務器在工作過程中有可能由于配置的改變而要重啟,或者是由于程序異常奔潰要重新啟動。在這種情況下,如果服務器監聽的 Socket 處于 TIME_WAIT 狀態,那么調用 bind 方法綁定 Socket 就會失敗。如果要等待 2MSL 時間,對于服務器來說是難以接受的。要想解決此問題,需要給監聽 Socket 設置 SO_REUSEADDR 選項。

Java 的 java.net.ServerSocket 類提供了 setReuseAddress 方法,可以用以設置 SO_REUSEADDR 選項,如下:

        ServerSocket ss = null;
        try {
            ss = new ServerSocket();
            ss.setReuseAddress(true);
            ss.bind(new InetSocketAddress(8022));
            ss.accept();
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            if (ss != null){
                try {
                    ss.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
  • 注意:
  • SO_REUSEADDR 選項需要在 bind 方法調用前設置,所以要創建一個未綁定的 ServerSocket 對象,然后手動執行 bind 操作。

4.2 SO_KEEPALIVE

SO_KEEPALIVE 是協議棧提供的一種連接?;顧C制,一般是用在 TCP 協議中。主要目的是當通信雙方長時間沒有數據交互,然而 Socket還沒有被關閉,協議棧會向對方發送一個 Heartbeat 消息期望對方回復一個 ACK,如果對方能回復說明連接是正常的,如果對方不能回復,嘗試幾次以后就會關閉連接。系統?;畹臅r間一般是 2 小時。

Java 的 java.net.Socket 類提供了 setKeepAlive 方法,可以用以設置 SO_KEEPALIVE 選項,如下:

    sock.setKeepAlive(true);

4.3 SO_LINGER

SO_LINGER 是用來設置“連接關閉以后,未發送完的數據包還可以在協議棧逗留的時間”。java.net.Socket 提供了 setSoLinger 方法可以設置 SO_LINGER 選項。原型如下:

public void setSoLinger(boolean on, int linger) throws SocketException
  • 如果設置 on 為 false,則該選項的值被忽略,協議棧會采用默認行為。close 調用會立即返回給調用者,協議棧會盡可能將 Socket 發送緩沖區未發送的數據發送完成。

  • 如果設置 on 為 true,但是 linger 為 0,當你調用了 close() 方法以后,協議棧將丟棄保留在 Socket 發送緩沖區中未發送完的數據,然后向對方發送一個 RST。這樣連接很快會被關閉,不會進入 TIME_WAIT 狀態,這也是一個避免“由于大量 TIME_WAIT 狀態的 Socket 導致連接失敗“的解決辦法。

  • 如果設置 on 為 true ,但是 linger 的取值大于 0,當你調用了 close() 方法以后,如果 Socket 發送緩沖區還有未發送完的數據,那么系統會等待一個指定的時間,close() 才返回。注意,這種情況下 close() 方法返回,并不能保證 Socket 發送緩沖區中未發送的數據被成功發送完。

  • 注意:
  • 參數 linger 的單位是。
sock.setSoLinger(true, 20);

4.4 SO_RCVBUF

SO_RCVBUF 很好理解,用于設置 Socket 的接收緩沖區大小。TCP 一般不需要設置,UDP 可能需要設置。java.net.Socket 類提供了 setReceiveBufferSize 方法可以設置接收緩沖區的大小。

sock.setReceiveBufferSize(16384);

4.5 SO_SNDBUF

SO_SNDBUF 也很好理解,用于設置 Socket 的發送緩沖區大小。一般不需要設置,采用系統默認大小即可。java.net.Socket 類提供了 setSendBufferSize 方法可以設置發送緩沖區的大小。

sock.setSendBufferSize(16384);

4.6 SO_OOBINLINE

SO_OOBINLINE 用于設置將“帶外數據”作為普通數據流來處理。java.net.Socket 類提供了 setOOBInline 方法可以設置 SO_OOBINLINE 選項。

sock.setOOBInline(true);

4.7 TCP_NODELAY

TCP_NODELAY 用于關閉 Nagle 算法,一般是用在實時性要求比較高的場景。java.net.Socket 提供了 setTcpNoDelay 方法用于設置 TCP_NODELAY 選項。

 sock.setTcpNoDelay(true);

5 小結

本節重點是介紹在 java 中設置常用 Socket 選項的方法。當然,我們是從 Linux 系統本身提供的 Socket 選項開始的,我們也介紹了在 linux 系統中如何查找 Socket 選項的方法。了解操作系統對 Socket 選項的支持,可以讓你形成一個完整的認識。

文中列出了常用 Socket 選項的應用場景。SO_REUSEADDR 是服務器必須要設置的一個選項,也只有服務器才需要此功能。TCP_NODELAY 是在開發實時性要求很高的程序時,必須要設置的,比如音視頻通信系統。

SO_LINGER 是在服務器端解決“由于 TIME_WAIT 過多,導致連接失敗的問題”時的一個常用方法。其他選項,可以根據需要選擇是否開啟。