在使用一個長連接的TCP時,如果TCP服務器端接收到TCP的客戶端連接過來后,接着服務器端的TCP節點需要對這個客戶端進行數據收發,收發時需要判斷這個SOCKET是否可用用,判斷方法有多種;
1.linux的5種方法,本人在使用modbus服務器端判斷已經連接的設備或是gprs服務器對已經連接的GPRS設備判斷,推薦使用方法2.
法一:
當recv()返回值小於等於0時,socket連接斷開。但是還需要判斷 errno是否等於 EINTR,如果errno == EINTR 則說明recv函數是由於程序接收到信號后返回的,socket連接還是正常的,不應close掉socket連接。
法二:
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返回1,recv返回0則斷開。其他注意事項同法一。
法四:
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錯誤
法五:
自己實現一個心跳檢測,一定時間內未收到自定義的心跳包則標記為已斷開。
-----------------------------------------------------------------
TCP狀態介紹
CLOSED:表示初始狀態。對服務端和C客戶端雙方都一樣。 LISTEN:表示監聽狀態。服務端調用了listen函數,可以開始accept連接了。 SYN_SENT:表示客戶端已經發送了SYN報文。當客戶端調用connect函數發起連接時,首先發SYN給服務端,然后自己進入SYN_SENT狀態,並等待服務端發送ACK+SYN。 SYN_RCVD:表示服務端收到客戶端發送SYN報文。服務端收到這個報文后,進入SYN_RCVD狀態,然后發送ACK+SYN給客戶端。 ESTABLISHED:表示連接已經建立成功了。服務端發送完ACK+SYN后進入該狀態,客戶端收到ACK后也進入該狀態。 FIN_WAIT_1:表示主動關閉連接。無論哪方調用close函數發送FIN報文都會進入這個這個狀態。 FIN_WAIT_2:表示被動關閉方同意關閉連接。主動關閉連接方收到被動關閉方返回的ACK后,會進入該狀態。 TIME_WAIT:表示收到對方的FIN報文並發送了ACK報文,就等2MSL后即可回到CLOSED狀態了。如果FIN_WAIT_1狀態下,收到對方同時帶FIN標志和ACK標志的報文時,可以直接進入TIME_WAIT狀態,而無須經過FIN_WAIT_2狀態。 CLOSING:表示雙方同時關閉連接。如果雙方幾乎同時調用close函數,那么會出現雙方同時發送FIN報文的情況,此時就會出現CLOSING狀態,表示雙方都在關閉連接。 CLOSE_WAIT:表示被動關閉方等待關閉。當收到對方調用close函數發送的FIN報文時,回應對方ACK報文,此時進入CLOSE_WAIT狀態。 LAST_ACK:表示被動關閉方發送FIN報文后,等待對方的ACK報文狀態,當收到ACK后進入CLOSED狀態。 特別提示的是:為什么TIME_WAIT狀態還需要等待2MSL才能回到CLOSED狀態?或者為什么TCP要引入TIME_WAIT狀態? 《TCP/IP詳解》中如此解釋:當TCP執行一個主動關閉,並發回最后一個ACK后,該連接必須在TIME_WAIT狀態停留的時間為2倍的MSL,這樣可以讓TCP再次發送最后的ACK以防止這個ACK丟失(另一端超時重發最后的FIN)。 附注:MSL(Maximum Segment Lifetime)即最大生存時間,RFC 793中指出MSL為2分鍾,但是實現中的常用值為30秒、1分鍾或者2分鍾。
MSL是Maximum Segment Lifetime英文的縮寫,中文可以譯為“報文最大生存時間”,他是任何報文在網絡上存在的最長時間,超過這個時間報文將被丟棄。因為tcp報文(segment)是ip數據報(datagram)的數據部分,具體稱謂請參見《數據在網絡各層中的稱呼》一文,而ip頭中有一個TTL域,TTL是time to live的縮寫,中文可以譯為“生存時間”,這個生存時間是由源主機設置初始值但不是存的具體時間,而是存儲了一個ip數據報可以經過的最大路由數,每經過一個處理他的路由器此值就減1,當此值為0則數據報將被丟棄,同時發送ICMP報文通知源主機。RFC 793中規定MSL為2分鍾,實際應用中常用的是30秒,1分鍾和2分鍾等。
2MSL即兩倍的MSL,TCP的TIME_WAIT狀態也稱為2MSL等待狀態,當TCP的一端發起主動關閉,在發出最后一個ACK包后,即第3次握手完成后發送了第四次握手的ACK包后就進入了TIME_WAIT狀態,必須在此狀態上停留兩倍的MSL時間,等待2MSL時間主要目的是怕最后一個ACK包對方沒收到,那么對方在超時后將重發第三次握手的FIN包,主動關閉端接到重發的FIN包后可以再發一個ACK應答包。在TIME_WAIT狀態時兩端的端口不能使用,要等到2MSL時間結束才可繼續使用。當連接處於2MSL等待階段時任何遲到的報文段都將被丟棄。不過在實際應用中可以通過設置SO_REUSEADDR選項達到不必等待2MSL時間結束再使用此端口。