一篇文章:css
23.1介绍html
在一个空闲的(idle)TCP链接上,没有任何的数据流,许多TCP/IP的初学者都对此感到惊奇。也就是说,若是TCP链接两端没有任何一个进程在向对方发送数据,那么在这两个TCP模块之间没有任何的数据交换。你可能在其它的网络协议中发现有轮询(polling),但在TCP中它不存在。言外之意就是咱们只要启动一个客户端进程,同服务器创建了TCP链接,无论你离开几小时,几天,几星期或是几个月,链接依旧存在。中间的路由器可能崩溃或者重启,电话线可能go down或者back up,只要链接两端的主机没有重启,链接依旧保持创建。linux
这就能够认为无论是客户端的仍是服务器端的应用程序都没有应用程序级(application-level)的定时器来探测链接的不活动状态(inactivity),从而引发任何一个应用程序的终止。回忆在10.7结束,BGP每隔30秒就向对方发送一个应用程序探测。这是一个应用程序定时器(application timer),与TCP存活定时器不一样。web
然而有的时候,服务器须要知道客户端主机是否已崩溃而且关闭,或者崩溃但重启。许多实现提供了存活定时器来完成这个任务。shell
存活(keepalive)并非TCP规范的一部分。在Host Requirements RFC罗列有不使用它的三个理由:(1)在短暂的故障期间,它们可能引发一个良好链接(good connection)被释放(dropped),(2)它们消费了没必要要的宽带,(3)在以数据包计费的互联网上它们(额外)花费金钱。然而,在许多的实现中提供了存活定时器。编程
存活定时器是一个包含争议的特征。许多人认为,即便须要这个特征,这种对对方的轮询也应该由应用程序来完成,而不是由TCP中实现。一些人对这个话题表现了极大的热情,甚至达到宗教般的狂热。服务器
若是两个终端系统之间的某个中间网络上有链接的暂时中断,那么存活选项(option)就可以引发两个进程间一个良好链接的终止。例如,若是正好在某个中间路由器崩溃、重启的时候发送存活探测,TCP就将会认为客户端主机已经崩溃,但事实并不是如此。网络
一些服务器应用程序可能表明客户端占用资源,它们须要知道客户端主机是否崩溃。存活定时器能够为这些应用程序提供探测服务。Telnet服务器和Rlogin服务器的许多版本都默认提供存活选项。app
我的计算机用户使用TCP/IP协议经过Telnet登陆一台主机,这是可以说明须要使用存活定时器的一个经常使用例子。若是某个用户在使用结束时只是关掉了电源,而没有注销(log off),那么他就留下了一个半打开(half-open)的链接。在图18.16,咱们看到如何在一个半打开链接上经过发送数据,获得一个复位(reset)返回,但那是在客户端,是由客户端发送的数据。若是客户端消失,留给了服务器端半打开的链接,而且服务器又在等待客户端的数据,那么等待将永远持续下去。存活特征的目的就是在服务器端检测这种半打开链接。socket
/* Net check Make sure you have not used OUT OF BAND DATA AND YOU CAN use OOB */ int netcheck(int fd) { int buf_size = 1024; char buf[buf_size]; //clear OOB DATA recv(fd, buf, buf_size); if(send(fd, (void *)"\0", 1, MSG_OOB) < 0 ) { fprintf(stderr, "Connection[%d] send OOB failed, %s", fd, strerror(errno)); return -1; } return 0; }
/* Setting SO_TCP KEEPALIVE */ //int keep_alive = 1;//设定KeepAlive //int keep_idle = 1;//开始首次KeepAlive探测前的TCP空闭时间 //int keep_interval = 1;//两次KeepAlive探测间的时间间隔 //int keep_count = 3;//断定断开前的KeepAlive探测次数 void set_keepalive(int fd, int keep_alive, int keep_idle, int keep_interval, int keep_count) { int opt = 1; if(keep_alive) { if(setsockopt(fd, SOL_SOCKET, SO_KEEPALIVE, (void*)&keep_alive, sizeof(keep_alive)) == -1) { fprintf(stderr, "setsockopt SOL_SOCKET::SO_KEEPALIVE failed, %s\n",strerror(errno)); } if(setsockopt(fd, SOL_TCP, TCP_KEEPIDLE, (void *)&keep_idle,sizeof(keep_idle)) == -1) { fprintf(stderr, "setsockopt SOL_TCP::TCP_KEEPIDLE failed, %s\n", strerror(errno)); } if(setsockopt(fd,SOL_TCP,TCP_KEEPINTVL, (void *)&keep_interval, sizeof(keep_interval)) == -1) { fprintf(stderr, "setsockopt SOL_tcp::TCP_KEEPINTVL failed, %s\n", strerror(errno)); } if(setsockopt(fd,SOL_TCP,TCP_KEEPCNT, (void *)&keep_count,sizeof(keep_count)) == -1) { fprintf(stderr, "setsockopt SOL_TCP::TCP_KEEPCNT failed, %s\n", strerror(errno)); } } }
这周在上班的路上看了本书《Effective TCP/IP Programming》,如下是一些读书笔记。顺带推荐一下这本书,写的很棒,适用于像我这样常常要写一些有必定质量的网络编程,但又没时间啃那些讲解TCPIP协议大部头书的人。
不少人都知道TCP并不会去主动检测链接的丢失,这意味着,若是双方不产生交互,那么若是网络断了或者有一方机器崩溃,另一方将永远不知道链接已经不可用了。检测链接是否丢失的方法大体有两种:keepalive和heart-beat。
Keepalive是不少的TCP实现提供的一种机制,它容许链接在空闲的时候双方会发送一些特殊的数据段,并经过响应与否来判断链接是否还存活着(所谓keep~~alive)。我曾经写过一篇关于keepalive的blog ,但后来我也发现,其实keepalive在实际的应用中并不常见。为什么如此?这得归结于keepalive设计的初衷。Keepalive适用于清除死亡时间比较长的链接。
好比这样的场景:一个用户建立tcp链接访问了一个web服务器,当用户完成他执行的操做后,很粗暴的直接拨了网线。这种状况下,这个tcp链接已经断开了,可是web服务器并不知道,它会依然守护着这个链接。若是web server设置了keepalive,那么它就可以在用户断开网线的大概几个小时之后,确认这个链接已经中断,而后丢弃此链接,回收资源。
采用keepalive,它会先要求此链接必定时间没有活动(通常是几个小时),而后发出数据段,通过屡次尝试后(每次尝试之间也有时间间隔),若是仍没有响应,则判断链接中断。可想而知,整个周期须要很长的时间。
因此,如前面的场景那样,须要一种方法可以清除和回收那些在系统不知情的状况下死去了好久的链接,keepalive是很是好的选择。
可是,在大部分状况下,特别是分布式环境中,咱们须要的是一个可以快速或者实时监控链接状态的机制,这里,heart-beat才是更加合适的方案。
Heart-beat(心跳),按个人理解,它的原理和keepalive很是相似,都是发送一个信号给对方,若是屡次发送都没有响应的话,则判断链接中断。它们的不一样点在于,keepalive是tcp实现中内建的机制,是在建立tcp链接时经过设置参数启动keepalive机制;而heart-beat则须要在tcp之上的应用层实现。一个简单的heart-beat实现通常测试链接是否中断采用的时间间隔都比较短,能够很快的决定链接是否中断。而且,因为是在应用层实现,由于能够自行决定当判断链接中断后应该采起的行为,而keepalive在判断链接失败后只会将链接丢弃。关于heart-beat,一个很是有趣的问题是,应该在传输真正数据的链接中发送“心跳”信号,仍是能够专门建立一个发送“心跳”信号的链接。好比说,A,B两台机器之间经过链接m来传输数据,如今为了可以检测A,B之间的链接状态,咱们是应该在链接m中传输“心跳”信号,仍是建立新的链接n来专门传输“心跳”呢?我我的认为二者皆可。若是担忧的是端到端的链接状态,那么就直接在该条链接中实现“心跳”。但不少时候,关注的是网络情况和两台主机间的链接状态,这种状况下, 建立专门的“心跳”链接也何尝不可。