Socket心跳包机制与实现

心跳包的发送,通常有两种技术

方法1:应用层自己实现的心跳包 

由应用程序自己发送心跳包来检测连接是否正常,大致的方法是:服务器在一个 Timer事件中定时 向客户端发送一个短小精悍的数据包,然后启动一个低级别的线程,在该线程中不断检测客户端的回应, 如果在一定时间内没有收到客户端的回应,即认为客户端已经掉线;同样,如果客户端在一定时间内没 有收到服务器的心跳包,则认为连接不可用。

方法2:TCP的KeepAlive保活机制

因为要考虑到一个服务器通常会连接多个客户端,因此由用户在应用层自己实现心跳包,代码较多 且稍显复杂,而利用TCP/IP协议层为内置的KeepAlive功能来实现心跳功能则简单得多。 不论是服务端还是客户端,一方开启KeepAlive功能后,就会自动在规定时间内向对方发送心跳包, 而另一方在收到心跳包后就会自动回复,以告诉对方我仍然在线。 因为开启KeepAlive功能需要消耗额外的宽带和流量,所以TCP协议层默认并不开启KeepAlive功 能,尽管这微不足道,但在按流量计费的环境下增加了费用,另一方面,KeepAlive设置不合理时可能会 因为短暂的网络波动而断开健康的TCP连接。并且,默认的KeepAlive超时需要7,200,000 MilliSeconds, 即2小时,探测次数为5次。对于很多服务端应用程序来说,2小时的空闲时间太长。因此,我们需要手工开启KeepAlive功能并设置合理的KeepAlive参数。

以上转自网络。

心跳包机制

跳包之所以叫心跳包是因为:它像心跳一样每隔固定时间发一次,以此来告诉服务器,这个客户端还活着。事实上这是为了保持长连接,至于这个包的内容,是没有什么特别规定的,不过一般都是很小的包,或者只包含包头的一个空包。

在TCP的机制里面,本身是存在有心跳包的机制的,也就是TCP的选项:SO_KEEPALIVE。系统默认是设置的2小时的心跳频率。但是它检查不到机器断电、网线拔出、防火墙这些断线。而且逻辑层处理断线可能也不是那么好处理。一般,如果只是用于保活还是可以的。

心跳包一般来说都是在逻辑层发送空的echo包来实现的。下一个定时器,在一定时间间隔下发送一个空包给客户端,然后客户端反馈一个同样的空包回来,服务器如果在一定时间内收不到客户端发送过来的反馈包,那就只有认定说掉线了。

其实,要判定掉线,只需要send或者recv一下,如果结果为零,则为掉线。但是,在长连接下,有可能很长一段时间都没有数据往来。理论上说,这个连接是一直保持连接的,但是实际情况中,如果中间节点出现什么故障是难以知道的。更要命的是,有的节点(防火墙)会自动把一定时间之内没有数据交互的连接给断掉。在这个时候,就需要我们的心跳包了,用于维持长连接,保活。

在获知了断线之后,服务器逻辑可能需要做一些事情,比如断线后的数据清理呀,重新连接呀……当然,这个自然是要由逻辑层根据需求去做了。

总的来说,心跳包主要也就是用于长连接的保活和断线处理。一般的应用下,判定时间在30-40秒比较不错。如果实在要求高,那就在6-9秒。

心跳检测步骤:

1客户端每隔一个时间间隔发生一个探测包给服务器

2客户端发包时启动一个超时定时器

3服务器端接收到检测包,应该回应一个包

4如果客户机收到服务器的应答包,则说明服务器正常,删除超时定时器

5如果客户端的超时定时器超时,依然没有收到应答包,则说明服务器挂了

转自:http://blog.sina.com.cn/s/blog_a459dcf5010153m5.html

根据上面的介绍我们可以知道对端以一种非优雅的方式断开连接的时候,我们可以设置SO_KEEPALIVE属性使得我们在2小时以后发现对方的TCP连接是否依然存在。

具体操作:

//设置KeepAlive

1、 BOOL   bKeepAlive   =   TRUE;

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

if(nRet!=0)

{

AfxMessageBox("出错"); 

return   ;

}

   2、感觉两小时时间太长可以自行设定方法1 

//设置KeepAlive检测时间和次数

tcp_keepalive    inKeepAlive   =   {0};   //输入参数

unsigned   long   ulInLen   =   sizeof(tcp_keepalive );

tcp_keepalive    outKeepAlive   =   {0};   //输出参数

unsigned   long   ulOutLen   =   sizeof(tcp_keepalive );

unsigned   long   ulBytesReturn   =   0;

//设置socket的keep   alive为10秒,并且发送次数为3次

inKeepAlive.onoff   =   1;

inKeepAlive.keepaliveinterval   =   4000;   //两次KeepAlive探测间的时间间隔

inKeepAlive.keepalivetime   =   1000;   //开始首次KeepAlive探测前的TCP空闭时间

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

if(SOCKET_ERROR   ==   nRet)

{

AfxMessageBox("出错");

return;

3、感觉两小时时间太长可以自行设定方法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

setsockopt的操作,详见:http://www.cnblogs.com/hateislove214/archive/2010/11/05/1869886.html



1.首先介绍下在队列中添加一个线程去doping.也就是做心跳包了.(这个不是我写的,有人说用定时器写会简单些)

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) {//设置断开连接,统一交到loop去处理connected_ =NO;            }                    });      }@finally{        [pool release];    }        }

}

3.开始说关键的地方了.上面这个代码

dispatch_barrier_async(_serialQueue, ^{if([selfsendPing] != kReturnNormal) {//设置断开连接,统一交到loop去处理connected_ =NO;            }                    });

本身代码就属于次线程,次线程中又添加了一个串行化队列.为什么要用串行化队列呢?

1.什么是串行化队列?

dispatch_barrier_async是在前面的任务执行结束后它才执行,而且它后面的任务等它执行完成之后才会执行

学习地址:http://www.cnblogs.com/stoic/archive/2012/09/25/2785184.html

因为后面防止发送数据,使用同一个方法,防止数据冲突.

这样的代码的前提

@property(nonatomic,strong)dispatch_queue_tserialQueue;//串行化队列

我之前是

@interfaceCommClient(){dispatch_queue_t_serialQueue;}

_serialQueue的创建.

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

我这个队列的属性的设置Strong,还是在interface 中添加,直接导致我的心跳包后面timeout.因为队列都丢失了.

作者:机器人小雪

链接:https://www.jianshu.com/p/90090e2f88d8

來源:简书

著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 205,132评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 87,802评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,566评论 0 338
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,858评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,867评论 5 368
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,695评论 1 282
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,064评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,705评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 42,915评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,677评论 2 323
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,796评论 1 333
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,432评论 4 322
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,041评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,992评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,223评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,185评论 2 352
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,535评论 2 343

推荐阅读更多精彩内容