簡體   English   中英

TCP 套接字連接是否具有“保持活動狀態”?

[英]Does a TCP socket connection have a “keep alive”?

我聽說過 HTTP keep-alive,但現在我想打開一個與遠程服務器的套接字連接。
現在這個套接字連接是否會永遠保持打開狀態,或者是否存在與 HTTP 保持活動類似的超時限制?

現在這個套接字連接是否會永遠保持打開狀態,或者是否存在與 HTTP 保持活動類似的超時限制?

簡短的回答是不,它不會永遠保持打開狀態,它可能會在幾個小時后超時。 所以超時,它是通過TCP保持活動的執行。

如果您想在您的機器上配置 Keep-Alive 超時,請參閱下面的“更改 TCP 超時”部分。 否則,請通讀答案的其余部分以了解 TCP Keep-Alive 的工作原理。

介紹

TCP 連接由兩個套接字組成,連接的每一端各一個。 當一側想要終止連接時,它發送一個RST數據包,另一側確認並關閉它們的套接字。

然而,在此之前,雙方將無限期地保持他們的插座打開。 這留下了一側可能有意或由於某些錯誤而關閉其套接字的可能性,而沒有通過RST通知另一端。 為了檢測這種情況並關閉過時的連接,使用了 TCP Keep Alive 進程。

保活過程

有三個可配置的屬性決定了 Keep-Alives 的工作方式。 在 Linux 上它們是1

  • tcp_keepalive_time
    • 默認 7200 秒
  • tcp_keepalive_probes
    • 默認 9
  • tcp_keepalive_intvl
    • 默認 75 秒

這個過程是這樣工作的:

  1. 客戶端打開 TCP 連接
  2. 如果連接靜默tcp_keepalive_time秒,則發送一個空的ACK數據包。 1
  3. 服務器是否以自己的相應ACK響應?
      1. 等待tcp_keepalive_intvl秒,然后再發送一個ACK
      2. 重復直到已發送的ACK探測數等於tcp_keepalive_probes
      3. 如果此時未收到響應,則發送RST並終止連接。
    • :返回第 2 步

大多數操作系統默認啟用此過程,因此一旦另一端無響應 2 小時 11 分鍾(7200 秒 + 75 * 9 秒),就會定期修剪死 TCP 連接。

陷阱

2 小時默認值

由於默認情況下連接空閑兩個小時后才會啟動該過程,因此過時的 TCP 連接在被修剪之前可能會停留很長時間。 這對於昂貴的連接(例如數據庫連接)尤其有害。

Keep-Alive 是可選的

根據RFC 1122 4.2.3.6 ,響應和/或中繼 TCP Keep-Alive 數據包是可選的

實現者可以在他們的 TCP 實現中包含“keep-alives”,盡管這種做法並沒有被普遍接受。 如果包含 keep-alives,應用程序必須能夠為每個 TCP 連接打開或關閉它們,並且它們必須默認為關閉。

...

記住不包含數據的 ACK 段不能被 TCP 可靠地傳輸是非常重要的。

原因是 Keep-Alive 數據包不包含任何數據,也不是絕對必要的,如果過度使用,可能會堵塞互聯網的管道。

然而在實踐中,我的經驗是,隨着帶寬變得更便宜,這種擔憂隨着時間的推移而減少。 因此,Keep-Alive 數據包通常不會被丟棄。 例如, Amazon EC2 文檔對 Keep-Alive 進行了間接認可,因此如果您使用 AWS 托管,您可能可以安全地依賴 Keep-Alive,但您的里程可能會有所不同。

更改 TCP 超時

每個插槽

不幸的是,由於 TCP 連接是在操作系統級別管理的,Java 不支持在每個套接字級別配置超時,例如在java.net.Socket 我發現了一些嘗試3使用 Java 本機接口 (JNI) 創建調用本機代碼來配置這些選項的 Java 套接字,但似乎沒有一個得到廣泛的社區采用或支持。

相反,您可能被迫將您的配置作為一個整體應用於操作系統。 請注意,此配置將影響整個系統上運行的所有 TCP 連接。

Linux

當前配置的 TCP Keep-Alive 設置可以在

  • /proc/sys/net/ipv4/tcp_keepalive_time
  • /proc/sys/net/ipv4/tcp_keepalive_probes
  • /proc/sys/net/ipv4/tcp_keepalive_intvl

您可以像這樣更新其中任何一個:

# Send first Keep-Alive packet when a TCP socket has been idle for 3 minutes
$ echo 180 > /proc/sys/net/ipv4/tcp_keepalive_time
# Send three Keep-Alive probes...
$ echo 3 > /proc/sys/net/ipv4/tcp_keepalive_probes
# ... spaced 10 seconds apart.
$ echo 10 > /proc/sys/net/ipv4/tcp_keepalive_intvl

此類更改不會在重新啟動后持續存在。 要進行持久更改,請使用sysctl

sysctl -w net.ipv4.tcp_keepalive_time=180 net.ipv4.tcp_keepalive_probes=3 net.ipv4.tcp_keepalive_intvl=10

Mac OS X

可以使用sysctl查看當前配置的設置:

$ sysctl net.inet.tcp | grep -E "keepidle|keepintvl|keepcnt"
net.inet.tcp.keepidle: 7200000
net.inet.tcp.keepintvl: 75000
net.inet.tcp.keepcnt: 8

值得注意的是,Mac OS X 以毫秒為單位定義了keepidlekeepintvl ,而不是 Linux 使用秒。

可以使用sysctl設置屬性,這將在重新啟動時保留這些設置:

sysctl -w net.inet.tcp.keepidle=180000 net.inet.tcp.keepcnt=3 net.inet.tcp.keepintvl=10000

或者,您可以將它們添加到/etc/sysctl.conf (如果文件不存在,則創建該文件)。

$ cat /etc/sysctl.conf
net.inet.tcp.keepidle=180000
net.inet.tcp.keepintvl=10000
net.inet.tcp.keepcnt=3

視窗

我沒有要確認的 Windows 機器,但您應該在注冊表中找到相應的 TCP Keep-Alive 設置:

\\HKEY_LOCAL_MACHINE\\System\\CurrentControlSet\\Services\\TCPIP\\Parameters

腳注

1. 查看man tcp了解更多信息。

2. 這個數據包通常被稱為“Keep-Alive”數據包,但在 TCP 規范中它只是一個普通的ACK數據包。 像 Wireshark 這樣的應用程序能夠通過元分析它包含的序列和確認號(參考套接字上的先前通信)將其標記為“Keep-Alive”數據包。

3. 我從基本的谷歌搜索中找到的一些例子是lucwilliams/JavaLinuxNetflonatel/libdontdie

TCP 套接字保持打開狀態,直到它們關閉。

也就是說,在不實際發送數據的情況下很難檢測到斷開的連接(斷開,如路由器死機等,而不是關閉),因此大多數應用程序每隔一段時間都會進行某種乒乓反應,以確保連接實際上仍然存在。

您正在尋找 SO_KEEPALIVE 套接字選項。

Java Socket API通過setKeepAlivegetKeepAlive方法向應用程序公開“keep-alive”。

編輯: SO_KEEPALIVE 在操作系統網絡協議棧中實現,而不發送任何“真實”數據。 保持活動間隔取決於操作系統,並且可以通過內核參數進行調整。

由於沒有數據發送,所以 SO_KEEPALIVE 只能測試網絡連接的活躍度,而不是套接字連接到的服務的活躍度。 要測試后者,您需要實現一些涉及向服務器發送消息並獲得響應的內容。

TCP keepalive 和 HTTP keepalive 是非常不同的概念。 在 TCP 中,keepalive 是發送用於檢測過時連接的管理數據包。 在 HTTP 中,keepalive 表示持久連接狀態。

這是來自 TCP 規范,

只有在一個時間間隔內沒有收到連接的數據或確認包時,才必須發送保持活動的包。 這個間隔必須是可配置的,並且必須默認不少於兩小時。

如您所見,默認 TCP keepalive 間隔對於大多數應用程序來說太長了。 您可能需要在應用程序協議中添加 keepalive。

如果您使用偽裝的 NAT(就像現在大多數家庭用戶一樣),則外部端口池是有限的,並且這些端口必須在 TCP 連接之間共享。 因此,如果在特定時間段內沒有發送數據,則偽裝 NAT 往往會假設連接已終止。

這個和其他類似的問題(兩個端點之間的任何地方)可能意味着如果您在合理的空閑期后嘗試發送數據,連接將不再“工作”。 但是,在您嘗試發送數據之前,您可能不會發現這一點。

使用 keepalive 既可以減少連接在某處被中斷的可能性,也可以讓您更快地發現連接中斷。

這里有一些關於 keepalive 的補充文獻,它更詳細地解釋了它。

http://www.tldp.org/HOWTO/html_single/TCP-Keepalive-HOWTO

由於 Java 不允許您控制實際的 keepalive 時間,因此如果您使用的是 Linux 內核(或基於 proc 的操作系統),則可以使用示例來更改它們。

在 JAVA Socket – TCP 連接是在 OS 級別管理的,java.net.Socket 不提供任何內置函數來設置每個套接字級別的 keepalive 數據包超時。 但是我們可以為 java socket 啟用 keepalive 選項,但默認情況下需要 2 小時 11 分鍾(7200 秒)來處理過時的 tcp 連接。 此原因連接將在清除之前可用很長時間。 因此,我們找到了一些使用 Java 本機接口 (JNI) 調用本機代碼 (c++) 來配置這些選項的解決方案。

****Windows 操作系統****

在 windows 操作系統中,keepalive_time & keepalive_intvl 可以配置,但 tcp_keepalive_probes 不能改變。默認情況下,當 TCP 套接字初始化時,將保持連接超時設置為 2 小時,保持連接間隔為 1 秒。 保持活動超時的默認系統范圍值可通過 KeepAliveTime 注冊表設置進行控制,該設置以毫秒為單位。

在 Windows Vista 和更高版本上,保持活動探測(數據重新傳輸)的數量設置為 10,並且無法更改。

在Windows Server 2003、Windows XP和Windows 2000上,keep-alive探針的數量默認設置為5。keep-alive探針的數量是可控的。 對於 windows Winsock IOCTLs 庫用於配置 tcp-keepalive 參數。

int WSAIoctl( SocketFD, // 標識套接字的描述符 SIO_KEEPALIVE_VALS, // dwIoControlCode (LPVOID) lpvInBuffer, // 指向 tcp_keepalive struct (DWORD) cbInBuffer, // 輸入緩沖區的長度 NULL, // 輸出緩沖區 0, // 的大小輸出緩沖區 (LPDWORD) lpcbBytesReturned, // 返回的字節數 NULL, // OVERLAPPED 結構 NULL // 完成例程 );

操作系統

Linux 內置了對 keepalive 的支持,需要啟用 TCP/IP 網絡才能使用它。 程序必須使用setsockopt 接口為其套接字請求keepalive 控制。

int setsockopt(int socket, int level, int optname, const void *optval, socklen_t optlen)

每個客戶端套接字都將使用 java.net.Socket 創建。 每個套接字的文件描述符 ID 將使用 java 反射檢索。

根據Microsoft 文檔,對於 Windows

  • KeepAliveTime(REG_DWORD,毫秒,默認情況下未設置,這意味着 7,200,000,000 = 2 小時) - 類似於 tcp_keepalive_time
  • KeepAliveInterval(REG_DWORD,毫秒,默認情況下未設置,這意味着 1,000 = 1 秒) - 類似於 tcp_keepalive_intvl
  • 由於 Windows Vista 沒有類似於 tcp_keepalive_probes 的值,因此值固定為 10 且無法更改

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM