tcp keepalive
部分信息可以看UNIX網(wǎng)絡(luò)編程第157頁(yè),
摘錄過來(lái):
在一個(gè)正常的TCP連接上,當(dāng)我們用無(wú)限等待的方式調(diào)用下面的Recv或Send的時(shí)候:
?? ret=recv(s,&buf[idx],nLeft,flags);
? ?或
?? ret=send(s,&buf[idx],nLeft,flags);
?? 如果TCP連接被對(duì)方正常關(guān)閉,也就是說(shuō),對(duì)方是正確地調(diào)用了closesocket(s)或者shutdown(s)的話,那么上面的Recv或Send調(diào)用就能馬上返回,并且報(bào)錯(cuò)。這是由于closesocket(s)或者shutdown(s)有個(gè)正常的關(guān)閉過程,會(huì)告訴對(duì)方“TCP連接已經(jīng)關(guān)閉,你不需要再發(fā)送或者接受消息了”。但是,如果是網(wǎng)線突然被拔掉,TCP連接的任何一端的機(jī)器突然斷電或重啟動(dòng),那么這時(shí)候正在執(zhí)行Recv或Send操作的一方就會(huì)因?yàn)闆]有任何連接中斷的通知而一直等待下去,也就是會(huì)被長(zhǎng)時(shí)間卡住。這種情形解決的辦法:
1>自己編寫心跳包程序
簡(jiǎn)單的說(shuō)也就是在自己的程序中加入一條線程,定時(shí)向?qū)Χ税l(fā)送數(shù)據(jù)包,查看是否有ACK,如果有則連接正常,沒有的話則連接斷開
2>啟動(dòng)TCP編程里的keepAlive機(jī)制
?
其實(shí)keepalive的原理就是TCP內(nèi)嵌的一個(gè)心跳包,
以服務(wù)器端為例,如果當(dāng)前server端檢測(cè)到超過一定時(shí)間(默認(rèn)是?7,200,000 milliseconds,也就是2個(gè)小時(shí))沒有數(shù)據(jù)傳輸,那么會(huì)向client端發(fā)送一個(gè)keep-alive packet(該keep-alive packet就是ACK和當(dāng)前TCP序列號(hào)減一的組合),此時(shí)client端應(yīng)該為以下三種情況之一:
?
1. client端仍然存在,網(wǎng)絡(luò)連接狀況良好。此時(shí)client端會(huì)返回一個(gè)ACK。server端接收到ACK后重置計(jì)時(shí)器,在2小時(shí)后再發(fā)送探測(cè)。如果2小時(shí)內(nèi)連接上有數(shù)據(jù)傳輸,那么在該時(shí)間基礎(chǔ)上向后推延2個(gè)小時(shí)。
2.?客戶端異常關(guān)閉,或是網(wǎng)絡(luò)斷開。在這兩種情況下,client端都不會(huì)響應(yīng)。服務(wù)器沒有收到對(duì)其發(fā)出探測(cè)的響應(yīng),并且在一定時(shí)間(系統(tǒng)默認(rèn)為1000 ms)后重復(fù)發(fā)送keep-alive packet,并且重復(fù)發(fā)送一定次數(shù)(2000 XP 2003?系統(tǒng)默認(rèn)為5次, Vista后的系統(tǒng)默認(rèn)為10次)。
3.?客戶端曾經(jīng)崩潰,但已經(jīng)重啟。這種情況下,服務(wù)器將會(huì)收到對(duì)其存活探測(cè)的響應(yīng),但該響應(yīng)是一個(gè)復(fù)位,從而引起服務(wù)器對(duì)連接的終止。
?
? 對(duì)于實(shí)用程序來(lái)說(shuō),2小時(shí)的空閑時(shí)間太長(zhǎng)。因此,我們需要手工開啟Keepalive功能并設(shè)置合理的Keepalive參數(shù)。在XP和WIN2003系統(tǒng)上,可以針對(duì)單獨(dú)的socket來(lái)設(shè)置,但是在windows?2000,不能單獨(dú)設(shè)置,如果設(shè)置,那么影響是整個(gè)系統(tǒng)的所有socket。
?
了解了keep alive大致的原理,下來(lái)看看在程序中怎么用,怎么設(shè)置參數(shù):
[cpp]?view plaincopy
其中,setsockopt設(shè)置了keepalive模式,但是系統(tǒng)對(duì)keepalive默認(rèn)的參數(shù)可能不符合我們的要求,比如空閑2小時(shí)后才探測(cè)對(duì)端是否活躍,所以WSAIoctl函數(shù)通過tcp_keepalive結(jié)構(gòu)體對(duì)這些參數(shù)進(jìn)行了相應(yīng)設(shè)置
tcp_keepalive這個(gè)結(jié)構(gòu)體在mstcpip.h頭文件中有定義:
[cpp]?view plaincopy
這個(gè)結(jié)構(gòu)體設(shè)置了空閑檢測(cè)時(shí)間,及檢測(cè)時(shí)重復(fù)發(fā)送的間隔時(shí)間
按照msdn上的說(shuō)法,這些參數(shù)也可以通過在注冊(cè)表里設(shè)置,分別為:
HKLM\SYSTEM\CurrentControlSet\Services\Tcpip\Parameters\KeepAliveTime
HKLM\SYSTEM\CurrentControlSet\Services\Tcpip\Parameters\KeepAliveInterval
?
另外,有些人可能已經(jīng)發(fā)現(xiàn)了,tcp_keepalive這個(gè)結(jié)構(gòu)體中沒有對(duì)重試次數(shù)這個(gè)參數(shù)的設(shè)置,這個(gè)參數(shù)可以通過注冊(cè)表來(lái)設(shè)置,具體位置為:
HKLM\SYSTEM\CurrentControlSet\Services\Tcpip\Parameters\TcpMaxDataRetransmissions
此處的keepalivetime表示的是TCP連接處于暢通時(shí)候的探測(cè)頻率,一旦探測(cè)包沒有返回,就以keepaliveinterval的頻率發(fā)送,經(jīng)過若干次的重試,如果探測(cè)包都沒有返回,那么就得出結(jié)論:TCP連接已經(jīng)斷開,于是上面的Recv或Send調(diào)用也就能馬上返回,不會(huì)無(wú)限制地卡住了。
? 上圖是對(duì)上面文字的說(shuō)明。亮條之前,TCP處于暢通狀態(tài),KeepAlive是以1000毫秒(keepalivetime的值)的頻率發(fā)送探測(cè)包,在發(fā)送到第32個(gè)探測(cè)包的時(shí)候,探測(cè)包沒有返回,于是就以5000毫秒(keepalivetime的值)的頻率發(fā)送探測(cè)包,重發(fā)幾次后,探測(cè)包都沒有返回,于是就得出結(jié)論:此TCP連接已經(jīng)斷開了!
?
設(shè)置好keepalive以后,我們通過實(shí)驗(yàn)來(lái)看看當(dāng)client異常退出或是網(wǎng)絡(luò)斷掉的情況下,keepalive怎么通知我們異常斷開的情況。這里采用select模式,實(shí)驗(yàn)環(huán)境為XP系統(tǒng)和Win7系統(tǒng),幾種情況返回值如下:
?
1.?正常斷開
select函數(shù)正常返回,recv函數(shù)返回0
?
2.?異常斷開
a)?????? 程序異常退出,如client端重啟,應(yīng)用非正常關(guān)閉等
select函數(shù)正常返回,recv函數(shù)返回SOCKET_ERROR,WSAGetLastError()得到的結(jié)果為WSAECONNRESET(10054)。
b)????? 網(wǎng)絡(luò)斷開
結(jié)果同上:select函數(shù)正常返回,recv函數(shù)返回SOCKET_ERROR,WSAGetLastError()得到的結(jié)果為WSAECONNRESET(10054)。
對(duì)于程序異常退出的情況,實(shí)際上在不開啟keepalive的情況下也是可以檢測(cè)到的
新人創(chuàng)作打卡挑戰(zhàn)賽發(fā)博客就能抽獎(jiǎng)!定制產(chǎn)品紅包拿不停!總結(jié)
以上是生活随笔為你收集整理的tcp keepalive的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: SQL Server 2008 允许远程
- 下一篇: C#复习⑧