Socket心跳包機(jī)制與實(shí)現(xiàn)

心跳包的發(fā)送,通常有兩種技術(shù)

方法1:應(yīng)用層自己實(shí)現(xiàn)的心跳包?

由應(yīng)用程序自己發(fā)送心跳包來(lái)檢測(cè)連接是否正常,大致的方法是:服務(wù)器在一個(gè) Timer事件中定時(shí) 向客戶端發(fā)送一個(gè)短小精悍的數(shù)據(jù)包,然后啟動(dòng)一個(gè)低級(jí)別的線程,在該線程中不斷檢測(cè)客戶端的回應(yīng), 如果在一定時(shí)間內(nèi)沒有收到客戶端的回應(yīng),即認(rèn)為客戶端已經(jīng)掉線;同樣,如果客戶端在一定時(shí)間內(nèi)沒 有收到服務(wù)器的心跳包,則認(rèn)為連接不可用。

方法2:TCP的KeepAlive?;顧C(jī)制

因?yàn)橐紤]到一個(gè)服務(wù)器通常會(huì)連接多個(gè)客戶端,因此由用戶在應(yīng)用層自己實(shí)現(xiàn)心跳包,代碼較多 且稍顯復(fù)雜,而利用TCP/IP協(xié)議層為內(nèi)置的KeepAlive功能來(lái)實(shí)現(xiàn)心跳功能則簡(jiǎn)單得多。 不論是服務(wù)端還是客戶端,一方開啟KeepAlive功能后,就會(huì)自動(dòng)在規(guī)定時(shí)間內(nèi)向?qū)Ψ桨l(fā)送心跳包, 而另一方在收到心跳包后就會(huì)自動(dòng)回復(fù),以告訴對(duì)方我仍然在線。 因?yàn)殚_啟KeepAlive功能需要消耗額外的寬帶和流量,所以TCP協(xié)議層默認(rèn)并不開啟KeepAlive功 能,盡管這微不足道,但在按流量計(jì)費(fèi)的環(huán)境下增加了費(fèi)用,另一方面,KeepAlive設(shè)置不合理時(shí)可能會(huì) 因?yàn)槎虝旱木W(wǎng)絡(luò)波動(dòng)而斷開健康的TCP連接。并且,默認(rèn)的KeepAlive超時(shí)需要7,200,000 MilliSeconds, 即2小時(shí),探測(cè)次數(shù)為5次。對(duì)于很多服務(wù)端應(yīng)用程序來(lái)說,2小時(shí)的空閑時(shí)間太長(zhǎng)。因此,我們需要手工開啟KeepAlive功能并設(shè)置合理的KeepAlive參數(shù)。

以上轉(zhuǎn)自網(wǎng)絡(luò)。

心跳包機(jī)制

跳包之所以叫心跳包是因?yàn)椋核裥奶粯用扛艄潭〞r(shí)間發(fā)一次,以此來(lái)告訴服務(wù)器,這個(gè)客戶端還活著。事實(shí)上這是為了保持長(zhǎng)連接,至于這個(gè)包的內(nèi)容,是沒有什么特別規(guī)定的,不過一般都是很小的包,或者只包含包頭的一個(gè)空包。

在TCP的機(jī)制里面,本身是存在有心跳包的機(jī)制的,也就是TCP的選項(xiàng):SO_KEEPALIVE。系統(tǒng)默認(rèn)是設(shè)置的2小時(shí)的心跳頻率。但是它檢查不到機(jī)器斷電、網(wǎng)線拔出、防火墻這些斷線。而且邏輯層處理斷線可能也不是那么好處理。一般,如果只是用于?;钸€是可以的。

心跳包一般來(lái)說都是在邏輯層發(fā)送空的echo包來(lái)實(shí)現(xiàn)的。下一個(gè)定時(shí)器,在一定時(shí)間間隔下發(fā)送一個(gè)空包給客戶端,然后客戶端反饋一個(gè)同樣的空包回來(lái),服務(wù)器如果在一定時(shí)間內(nèi)收不到客戶端發(fā)送過來(lái)的反饋包,那就只有認(rèn)定說掉線了。

其實(shí),要判定掉線,只需要send或者recv一下,如果結(jié)果為零,則為掉線。但是,在長(zhǎng)連接下,有可能很長(zhǎng)一段時(shí)間都沒有數(shù)據(jù)往來(lái)。理論上說,這個(gè)連接是一直保持連接的,但是實(shí)際情況中,如果中間節(jié)點(diǎn)出現(xiàn)什么故障是難以知道的。更要命的是,有的節(jié)點(diǎn)(防火墻)會(huì)自動(dòng)把一定時(shí)間之內(nèi)沒有數(shù)據(jù)交互的連接給斷掉。在這個(gè)時(shí)候,就需要我們的心跳包了,用于維持長(zhǎng)連接,?;睢?/p>

在獲知了斷線之后,服務(wù)器邏輯可能需要做一些事情,比如斷線后的數(shù)據(jù)清理呀,重新連接呀……當(dāng)然,這個(gè)自然是要由邏輯層根據(jù)需求去做了。

總的來(lái)說,心跳包主要也就是用于長(zhǎng)連接的?;詈蛿嗑€處理。一般的應(yīng)用下,判定時(shí)間在30-40秒比較不錯(cuò)。如果實(shí)在要求高,那就在6-9秒。

心跳檢測(cè)步驟:

1客戶端每隔一個(gè)時(shí)間間隔發(fā)生一個(gè)探測(cè)包給服務(wù)器

2客戶端發(fā)包時(shí)啟動(dòng)一個(gè)超時(shí)定時(shí)器

3服務(wù)器端接收到檢測(cè)包,應(yīng)該回應(yīng)一個(gè)包

4如果客戶機(jī)收到服務(wù)器的應(yīng)答包,則說明服務(wù)器正常,刪除超時(shí)定時(shí)器

5如果客戶端的超時(shí)定時(shí)器超時(shí),依然沒有收到應(yīng)答包,則說明服務(wù)器掛了

轉(zhuǎn)自:http://blog.sina.com.cn/s/blog_a459dcf5010153m5.html

根據(jù)上面的介紹我們可以知道對(duì)端以一種非優(yōu)雅的方式斷開連接的時(shí)候,我們可以設(shè)置SO_KEEPALIVE屬性使得我們?cè)?小時(shí)以后發(fā)現(xiàn)對(duì)方的TCP連接是否依然存在。

具體操作:

//設(shè)置KeepAlive

1、?BOOL?? bKeepAlive?? =?? TRUE;

int nRet=::setsockopt(sockClient,SOL_SOCKET,SO_KEEPALIVE,(char*)&bKeepAlive,sizeof(bKeepAlive));

if(nRet!=0)

{

AfxMessageBox("出錯(cuò)");?

return?? ;

}

?? 2、感覺兩小時(shí)時(shí)間太長(zhǎng)可以自行設(shè)定方法1?

//設(shè)置KeepAlive檢測(cè)時(shí)間和次數(shù)

tcp_keepalive??? inKeepAlive?? =?? {0};?? //輸入?yún)?shù)

unsigned?? long?? ulInLen?? =?? sizeof(tcp_keepalive );

tcp_keepalive??? outKeepAlive?? =?? {0};?? //輸出參數(shù)

unsigned?? long?? ulOutLen?? =?? sizeof(tcp_keepalive );

unsigned?? long?? ulBytesReturn?? =?? 0;

//設(shè)置socket的keep?? alive為10秒,并且發(fā)送次數(shù)為3次

inKeepAlive.onoff?? =?? 1;

inKeepAlive.keepaliveinterval?? =?? 4000;?? //兩次KeepAlive探測(cè)間的時(shí)間間隔

inKeepAlive.keepalivetime?? =?? 1000;?? //開始首次KeepAlive探測(cè)前的TCP空閉時(shí)間

nRet=WSAIoctl(sockClient,SIO_KEEPALIVE_VALS,(LPVOID)&inKeepAlive,ulInLen(LPVOID)&outKeepAlive,ulOutLen,&ulBytesReturn,NULL,NULL);

if(SOCKET_ERROR?? ==?? nRet)

{

AfxMessageBox("出錯(cuò)");

return;

}?

3、感覺兩小時(shí)時(shí)間太長(zhǎng)可以自行設(shè)定方法2

因此我們可以得到

int? ?? ?? ?? ?? ???keepIdle = 6;

int? ?? ?? ?? ?? ???keepInterval = 5;

int? ?? ?? ?? ?? ???keepCount = 3;

Setsockopt(listenfd, SOL_TCP, TCP_KEEPIDLE, (void *)&keepIdle, sizeof(keepIdle));

Setsockopt(listenfd, SOL_TCP,TCP_KEEPINTVL, (void *)&keepInterval, sizeof(keepInterval));

Setsockopt(listenfd,SOL_TCP, TCP_KEEPCNT, (void *)&keepCount, sizeof(keepCount));

詳見:http://blog.csdn.net/gavin1203/article/details/5290609

對(duì)setsockopt的操作,詳見:http://www.cnblogs.com/hateislove214/archive/2010/11/05/1869886.html



1.首先介紹下在隊(duì)列中添加一個(gè)線程去doping.也就是做心跳包了.(這個(gè)不是我寫的,有人說用定時(shí)器寫會(huì)簡(jiǎn)單些)

NSInvocationOperation*pingop =[[NSInvocationOperationalloc] initWithTarget:selfselector:@selector(doPing)? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? object:nil];[gQueue_ addOperation:pingop];[pingop release];

2.介紹下具體的doping 方法

- (void)doPing {floatinterval = [Settings instance].appInfo.pingInterval;constfloatwait =0.5f;while(connected_) {NSAutoreleasePool* pool = [[NSAutoreleasePoolalloc] init];@try{floatpast =0.0;while(connected_ && past < interval) {? ? ? ? ? ? [NSThreadsleepForTimeInterval:wait];? ? ? ? ? ? past += wait;? ? ? ? }if(!connected_) {break;? ? ? ? }? ? ? ? dispatch_barrier_async(_serialQueue, ^{if([selfsendPing] != kReturnNormal) {//設(shè)置斷開連接,統(tǒng)一交到loop去處理connected_ =NO;? ? ? ? ? ? }? ? ? ? ? ? ? ? ? ? });? ? ? }@finally{? ? ? ? [pool release];? ? }? ? ? ? }

}

3.開始說關(guān)鍵的地方了.上面這個(gè)代碼

dispatch_barrier_async(_serialQueue, ^{if([selfsendPing] != kReturnNormal) {//設(shè)置斷開連接,統(tǒng)一交到loop去處理connected_ =NO;? ? ? ? ? ? }? ? ? ? ? ? ? ? ? ? });

本身代碼就屬于次線程,次線程中又添加了一個(gè)串行化隊(duì)列.為什么要用串行化隊(duì)列呢?

1.什么是串行化隊(duì)列?

dispatch_barrier_async是在前面的任務(wù)執(zhí)行結(jié)束后它才執(zhí)行,而且它后面的任務(wù)等它執(zhí)行完成之后才會(huì)執(zhí)行

學(xué)習(xí)地址:http://www.cnblogs.com/stoic/archive/2012/09/25/2785184.html

因?yàn)楹竺娣乐拱l(fā)送數(shù)據(jù),使用同一個(gè)方法,防止數(shù)據(jù)沖突.

這樣的代碼的前提

@property(nonatomic,strong)dispatch_queue_tserialQueue;//串行化隊(duì)列

我之前是

@interfaceCommClient(){dispatch_queue_t_serialQueue;}

_serialQueue的創(chuàng)建.

_serialQueue = dispatch_queue_create("com.navensoft.eric",NULL);

我這個(gè)隊(duì)列的屬性的設(shè)置Strong,還是在interface 中添加,直接導(dǎo)致我的心跳包后面timeout.因?yàn)殛?duì)列都丟失了.

作者:機(jī)器人小雪

鏈接:http://www.itdecent.cn/p/90090e2f88d8

來(lái)源:簡(jiǎn)書

著作權(quán)歸作者所有。商業(yè)轉(zhuǎn)載請(qǐng)聯(lián)系作者獲得授權(quán),非商業(yè)轉(zhuǎn)載請(qǐng)注明出處。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • 1、TCP狀態(tài)linux查看tcp的狀態(tài)命令:1)、netstat -nat 查看TCP各個(gè)狀態(tài)的數(shù)量2)、lso...
    北辰青閱讀 9,700評(píng)論 0 11
  • <轉(zhuǎn)自CSDN技術(shù)博客>心跳包的發(fā)送,通常有兩種技術(shù)方法1:應(yīng)用層自己實(shí)現(xiàn)的心跳包由應(yīng)用程序自己發(fā)送心跳包來(lái)檢測(cè)連...
    魏成閱讀 1,764評(píng)論 0 1
  • UDP:用戶數(shù)據(jù)報(bào)協(xié)議:主要用在實(shí)時(shí)性要求比較高的以及對(duì)質(zhì)量相對(duì)較弱的地方.但是面對(duì)現(xiàn)在高質(zhì)量的線路不會(huì)容易丟包,...
    CoderZS閱讀 7,506評(píng)論 0 24
  • 地址:http://www.cnblogs.com/taoxu/p/7064103.html 寫在準(zhǔn)備動(dòng)手的時(shí)候:...
    wvqusrtg閱讀 3,139評(píng)論 1 19
  • 所以我已一個(gè)一般的嵌入式web服務(wù)器boa為原形進(jìn)行了從寫,專門適應(yīng)嵌入式web服務(wù)器這種需要。服務(wù)器這種需要。 ...
    碼農(nóng)不器閱讀 590評(píng)論 0 0

友情鏈接更多精彩內(nèi)容