TCP keepalive的详解(解惑)
TCP是面向連接的,一般情況,兩端的應用程序可以通過發送和接收數據得知對端的存活。
當兩端的應用程序都沒有數據發送和接收時,如何判斷連接是否正常呢?
這就是SO_KEEPALIVE的作用。
1. SO_KEEPALIVE 的作用
1.1 SO_KEEPALIVE的定義
SO_KEEPALIVE用于開啟或者關閉保活探測,默認情況下是關閉的。
當SO_KEEPALIVE開啟時,可以保持連接檢測對方主機是否崩潰,避免(服務器)永遠阻塞于TCP連接的輸入。
相關的屬性包括:
tcp_keepalive_time、tcp_keepalive_probes、tcp_keepalive_intvl。
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
這些屬性可以在/proc/sys/net/ipv4/下查看:
cat /proc/sys/net/ipv4/tcp_keepalive_time 7200 cat /proc/sys/net/ipv4/tcp_keepalive_probes 9 cat /proc/sys/net/ipv4/tcp_keepalive_intvl 75- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
也可以通過命令行查看:
sudo sysctl -a | grep keepalive net.ipv4.tcp_keepalive_time = 7200 net.ipv4.tcp_keepalive_probes = 9 net.ipv4.tcp_keepalive_intvl = 75- 1
- 2
- 3
- 4
- 5
1.2 連接探活的過程
開啟SO_KEEPALIVE后,如果2小時內在此套接口的任一方向都沒有數據交換,TCP就自動給對方發一個保持存活探測分節(keepalive probe)。這是一個對方必須響應的TCP分節.
它會導致以下三種情況:
- 對方接收一切正常:以期望的ACK響應。2小時后,TCP將發出另一個探測分節。
- 對方已崩潰且已重新啟動:以RST響應。套接口的待處理錯誤被置為ECONNRESET,套接 口本身則被關閉。
- 對方無任何響應:源自berkeley的TCP發送另外8個探測分節,相隔75秒一個,試圖得到一個響應。一共嘗試9次,即在發出第一個探測分節11分鐘 15秒后若仍無響應就放棄。套接口的待處理錯誤被置為ETIMEOUT,套接口本身則被關閉。如ICMP錯誤是“host unreachable(主機不可達)”,說明對方主機并沒有崩潰,但是不可達,這種情況下待處理錯誤被置為 EHOSTUNREACH。
根據上面的介紹我們可以知道對端以一種非優雅的方式斷開連接的時候,我們可以設置SO_KEEPALIVE屬性使得我們在2小時以后發現對方的TCP連接是否依然存在。
int keepAlive = 1; setsockopt(listenfd, SOL_SOCKET, SO_KEEPALIVE, (void*)&keepAlive, sizeof(keepAlive));- 1
- 2
- 3
如果我們不能接受如此之長的等待時間,怎么辦?
2.設置TCP KEEPALIVE
上面提到,SO_KEEPALIVE默認的時間間隔太長,不利于應用程序檢測連接狀態。
解決方法有2種:
- 全局設置
- 針對單個連接設置
2.1 全局設置
在Linux中我們可以通過修改 /etc/sysctl.conf 的全局配置:
net.ipv4.tcp_keepalive_time=7200 net.ipv4.tcp_keepalive_intvl=75 net.ipv4.tcp_keepalive_probes=9- 1
- 2
- 3
添加上面的配置后輸入 sysctl -p 使其生效,
你可以使用命令來查看當前的默認配置
- 1
如果應用中已經設置SO_KEEPALIVE,程序不用重啟,內核直接生效.
這種方法設置的全局的參數,針對整個系統生效,對單個socket的設置不夠友好。
2.2 針對單個連接設置
我們可以使用TCP的TCP_KEEPCNT、TCP_KEEPIDLE、TCP_KEEPINTVL3個選項。
這些選項是連接級別的,每個socket都可以設置這些屬性。
這些選項的定義,可以通過man查看。
man 7 tcp- 1
socket option:
TCP_KEEPCNT (since Linux 2.4)The maximum number of keepalive probes TCP should send beforedropping the connection. This option should not be used incode intended to be portable.關閉一個非活躍連接之前的最大重試次數。該選項不具備可移植性。 TCP_KEEPIDLE (since Linux 2.4)The time (in seconds) the connection needs to remain idlebefore TCP starts sending keepalive probes, if the socketoption SO_KEEPALIVE has been set on this socket. This optionshould not be used in code intended to be portable.設置連接上如果沒有數據發送的話,多久后發送keepalive探測分組,單位是秒該選項不具備可移植性。 TCP_KEEPINTVL (since Linux 2.4)The time (in seconds) between individual keepalive probes.This option should not be used in code intended to beportable.前后兩次探測之間的時間間隔,單位是秒該選項不具備可移植性。- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
代碼層面的設置步驟:
int keepAlive = 1; // 非0值,開啟keepalive屬性 int keepIdle = 60; // 如該連接在60秒內沒有任何數據往來,則進行此TCP層的探測 int keepInterval = 5; // 探測發包間隔為5秒 int keepCount = 3; // 嘗試探測的最多次數 // 開啟探活 setsockopt(sockfd, SOL_SOCKET, SO_KEEPALIVE, (void *)&keepAlive, sizeof(keepAlive)); setsockopt(sockfd, SOL_TCP, TCP_KEEPIDLE, (void*)&keepIdle, sizeof(keepIdle)); setsockopt(sockfd, SOL_TCP, TCP_KEEPINTVL, (void *)&keepInterval, sizeof(keepInterval)); setsockopt(sockfd, SOL_TCP, TCP_KEEPCNT, (void *)&keepCount, sizeof(keepCount)- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
3.為什么應用層需要heart beat/心跳包?
通過上面的介紹,感覺TCP keepalive已經很牛逼了,但為什么還會提到應用層的心跳呢?
目前了解的原因包括兩個:
- TCP keepalive處于傳輸層,由操作系統負責,能夠判斷進程存在,網絡通暢,但無法判斷進程阻塞或死鎖等問題。
- 客戶端與服務器之間有四層代理或負載均衡,即在傳輸層之上的代理,只有傳輸層以上的數據才被轉發,例如socks5等
所以,基于以上原因,有時候還是需要應用程序自己去設計心跳規則的。
可以服務端負責周期發送心跳包,檢測客戶端,也可以客戶端負責發送心跳包,或者服服務端和客戶端同時發送心跳包。
可以根據具體的應用場景進行設計。
參考
《UNIX網絡編程卷1》
《Linux多線程服務端編程》
man 7 tcp
setsockopt, SO_KEEPALIVE and Heartbeats
為什么基于TCP的應用需要心跳包(TCP keep-alive原理分析
TCP中已有SO_KEEPALIVE選項,為什么還要在應用層加入心跳包機制
微信的大規模使用真的會過多占用信令,影響通訊穩定嗎
總結
以上是生活随笔為你收集整理的TCP keepalive的详解(解惑)的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Go的自动化测试
- 下一篇: golang tcp keepalive