linux C语言 socket如何判断socket_fd对应的socket是否断开?(是否连通、是否正常连接)recv()、tcp_info TCP_ESTABLISHED、keepalive
文章目錄
- 判斷socket連接斷開的方法
- 法一:判斷recv()返回值
- 但是參考這篇文章的代碼,程序居然被阻塞了,不知道怎么回事
- 法二:創建tcp_info結構體,判斷info.tcpi_state是否為TCP_ESTABLISHED(注意:需包含tcp.h)
- 法三:用select函數的方法(沒太看懂)
- 法四:用keepalive屬性
- 說明
- 有關SO_KEEPALIVE的三個參數詳細解釋如下
- 法五:自己實現心跳檢測
判斷socket連接斷開的方法
法一:判斷recv()返回值
當recv()返回值小于等于0時,socket連接斷開。但是還需要判斷 errno是否等于 EINTR,如果errno == EINTR 則說明recv函數是由于程序接收到信號后返回的,socket連接還是正常的,不應close掉socket連接。
但是參考這篇文章的代碼,程序居然被阻塞了,不知道怎么回事
參考文章:如何在C語言中判斷socket是否已經斷開
#include <errno.h> bool IsSocketClosed(int clientSocket) { char buff[32]; int recvBytes = recv(clientSocket, buff, sizeof(buff), MSG_PEEK); int sockErr = errno; //cout << "In close function, recv " << recvBytes << " bytes, err " << sockErr << endl; if( recvBytes > 0) //Get data return false; if( (recvBytes == -1) && (sockErr == EWOULDBLOCK) ) //No receive data return false; return true; }法二:創建tcp_info結構體,判斷info.tcpi_state是否為TCP_ESTABLISHED(注意:需包含tcp.h)
struct tcp_info info; int len=sizeof(info); getsockopt(sock, IPPROTO_TCP, TCP_INFO, &info, (socklen_t *)&len); if((info.tcpi_state==TCP_ESTABLISHED)) //則說明未斷開 else 斷開法三:用select函數的方法(沒太看懂)
若使用了select等系統函數,若遠端斷開,則select返回1,recv返回0則斷開。其他注意事項同法一。
法四:用keepalive屬性
int keepAlive = 1; // 開啟keepalive屬性
int keepIdle = 60; // 如該連接在60秒內沒有任何數據往來,則進行探測
int keepInterval = 5; // 探測時發包的時間間隔為5 秒
int keepCount = 3; // 探測嘗試的次數.如果第1次探測包就收到響應了,則后2次的不再發.
setsockopt(rs, SOL_SOCKET, SO_KEEPALIVE, (void )&keepAlive, sizeof(keepAlive));
setsockopt(rs, SOL_TCP, TCP_KEEPIDLE, (void)&keepIdle, sizeof(keepIdle));
setsockopt(rs, SOL_TCP, TCP_KEEPINTVL, (void *)&keepInterval, sizeof(keepInterval));
setsockopt(rs, SOL_TCP, TCP_KEEPCNT, (void *)&keepCount, sizeof(keepCount));
設置后,若斷開,則在使用該socket讀寫時立即失敗,并返回ETIMEDOUT錯誤
說明
socket心跳機制so_keepalive的三個參數詳解
SO_KEEPALIVE 保持連接檢測對方主機是否崩潰,避免(服務器)永遠阻塞于TCP連接的輸入。
設置該選項后,如果2小時內在此套接口的任一方向都沒有數據交換,TCP就自動給對方 發一個保持存活探測分節(keepalive probe)。這是一個對方必須響應的TCP分節.它會導致以下三種情況:
1、對方接收一切正常:以期望的ACK響應,2小時后,TCP將發出另一個探測分節。
2、對方已崩潰且已重新啟動:以RST響應。套接口的待處理錯誤被置為ECONNRESET,套接 口本身則被關閉。
3、對方無任何響應:源自berkeley的TCP發送另外8個探測分節,相隔75秒一個,試圖得到一個響應。在發出第一個探測分節11分鐘15秒后若仍無響應就放棄。套接口的待處理錯誤被置為ETIMEOUT,套接口本身則被關閉。如ICMP錯誤是“host unreachable(主機不可達)”,說明對方主機并沒有崩潰,但是不可達,這種情況下待處理錯誤被置為 EHOSTUNREACH。
有關SO_KEEPALIVE的三個參數詳細解釋如下
(16)tcp_keepalive_intvl,保活探測消息的發送頻率。默認值為75s。
發送頻率tcp_keepalive_intvl乘以發送次數tcp_keepalive_probes,就得到了從開始探測直到放棄探測確定連接斷開的時間,大約為11min。
(17)tcp_keepalive_probes,TCP發送保活探測消息以確定連接是否已斷開的次數。默認值為9(次)。
注意:只有設置了SO_KEEPALIVE套接口選項后才會發送保活探測消息。
(18)tcp_keepalive_time,在TCP保活打開的情況下,最后一次數據交換到TCP發送第一個保活探測消息的時間,即允許的持續空閑時間。默認值為7200s(2h)。
法五:自己實現心跳檢測
自己實現一個心跳檢測,一定時間內未收到自定義的心跳包則標記為已斷開。
TCP中已有SO_KEEPALIVE選項,為什么還要在應用層加入心跳包機制?
首先,我想說的是,SO_Keeplive是實現在服務器側,客戶端被動響應,缺省超時時間為120分鐘,這是RFC協議標準規范。
SO_Keeplive是實現在TCP協議棧(四層),應用層的心跳實現在第七層,本質沒有任何區別,但應用層需要自己來定義心跳包格式。
之所以實現在服務器側,是因為與客戶端相比,服務器側的壽命更長,因為服務器側需要不間斷地提供服務,而客戶端可能由于用戶下班而合上電腦(TCP沒有來得及發送FIN關閉連接),這樣的話,服務器側就會有很多不可用的TCP連接(established),這樣的連接依然會占用服務器內存資源,于是就設計這個keepalive 來檢測客戶端是否可用,如果幾次重傳keepalive ,客戶端沒有相應,刪除連接,釋放資源。
需要指出的是,超時時間是指TCP連接沒有任何數據、控制字傳輸的時間,如果有任何數據傳輸,會刷新定時器,重新走表。
TCP心跳是一個備受爭議的實現,只是一個option,不是強制標準。
之所以應用層需要獨立實現自己的心跳,是因為超時時間較長,無法給應用層提供快速的反饋。
所以類似BGP協議就獨立實現了自己的keepalive,最小可以設置一秒鐘,三次沒有應答即可以Reset連接,最快三秒可以檢測到失效。
而三秒依然太慢,可以用另外一個協議BFD來提供更快發現鏈路失效,最快可以配置成10ms
,三次超時(30ms)就可以完成失效檢測。
區別
tcp keepalive檢查連接是否存活。
應用keppalive檢測應用是否正常可響應。
舉個例子。服務端死鎖,無法處理任何業務請求。但是操作系統仍然可以響應網絡層keepalive包。
參考文章:判斷socket連接斷開的方法
與50位技術專家面對面20年技術見證,附贈技術全景圖總結
以上是生活随笔為你收集整理的linux C语言 socket如何判断socket_fd对应的socket是否断开?(是否连通、是否正常连接)recv()、tcp_info TCP_ESTABLISHED、keepalive的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: socket通信流程图
- 下一篇: linux C语言如何获取进程号和线程号