tcp/心跳包

1,http://blog.csdn.net/yuzhiyuxia/article/details/7857508css

心跳包就是在客户端和服务器间定时通知对方本身状态的一个本身定义的命令字,按照必定的时间间隔发送,相似于心跳,因此叫作心跳包。   
     用来判断对方(设备,进程或其它网元)是否正常运行,采用定时发送简单的通信包,若是在指定时间段内未收到对方响应,则判断对方已经离线。用于检测TCP的异常断开。基本缘由是服务器端不能有效的判断客户端是否在线,也就是说,服务器没法区分客户端是长时间在空闲,仍是已经掉线的状况。所谓的心跳包就是客户端定时发送简单的信息给服务器端告诉它我还在而已。代码就是每隔几分钟发送一个固定信息给服务端,服务端收到后回复一个固定信息若是服务端几分钟内没有收到客户端信息则视客户端断开。   好比有些通讯软件长时间不使用,要想知道它的状态是在线仍是离线就须要心跳包,定时发包收包。发包方:能够是客户也能够是服务端,看哪边实现方便合理,通常是客户端。服务器也能够定时发心跳下去。通常来讲,出于效率的考虑,是由客户端主动向服务器端发包,而不是服务器向客户端发。客户端每隔一段时间发一个包,使用TCP的,用send发,使用UDP的,用sendto发,服务器收到后,就知道当前客户端还处于“活着”的状态,不然,若是隔必定时间未收到这样的包,则服务器认为客户端已经断开,进行相应的客户端断开逻辑处理。
 
       心跳包之因此叫心跳包是由于:它像心跳同样每隔固定时间发一次,以此来告诉服务器,这个客户端还活着。事实上这是为了保持长链接,至于这个包的内容,是没有什么特别规定的,不过通常都是很小的包,或者只包含包头的一个空包。
      在TCP/IP的机制里面,自己是存在有心跳包的机制的,也就是TCP的选项:SO_KEEPALIVE。系统默认是设置的2小时的心跳频率。可是它检查不到机器断电、网线拔出、防火墙这些断线。并且逻辑层处理断线可能也不是那么好处理。通常,若是只是用于保活仍是能够的。
    心跳包通常来讲都是在逻辑层发送空的echo包来实现的。下一个定时器,在必定时间间隔下发送一个空包给客户端,而后客户端反馈一个一样的空包回来,服务器若是在必定时间内收不到客户端发送过来的反馈包,那就只有认定说掉线了。
       其实,要断定掉线,只须要send或者recv一下,若是结果为零,则为掉线。可是,在长链接下,有可能很长一段时间都没有数据往来。理论上说,这个链接是一直保持链接的,可是实际状况中,若是中间节点出现什么故障是难以知道的。更要命的是,有的节点(防火墙)会自动把必定时间以内没有数据交互的链接给断掉。在这个时候,就须要咱们的心跳包了,用于维持长链接,保活。
       在获知了断线以后,服务器逻辑可能须要作一些事情,好比断线后的数据清理呀,从新链接呀……固然,这个天然是要由逻辑层根据需求去作了。总的来讲,心跳包主要也就是用于长链接的保活和断线处理。通常的应用下,断定时间在30-40秒比较不错。若是实在要求高,那就在6-9秒.
 
2, http://blog.csdn.net/xuyuefei1988/article/details/8279812
心跳包的发送,一般有两种技术
方法1:应用层本身实现的心跳包 
由应用程序本身发送心跳包来检测链接是否正常,大体的方法是:服务器在一个 Timer事件中定时 向客户端发送一个短小精悍的数据包,而后启动一个低级别的线程,在该线程中不断检测客户端的回应, 若是在必定时间内没有收到客户端的回应,即认为客户端已经掉线;一样,若是客户端在必定时间内没 有收到服务器的心跳包,则认为链接不可用。

方法2:TCP的KeepAlive保活机制
由于要考虑到一个服务器一般会链接多个客户端,所以由用户在应用层本身实现心跳包,代码较多 且稍显复杂,而利用TCP/IP协议层为内置的KeepAlive功能来实现心跳功能则简单得多。 不管是服务端仍是客户端,一方开启KeepAlive功能后,就会自动在规定时间内向对方发送心跳包, 而另外一方在收到心跳包后就会自动回复,以告诉对方我仍然在线。 由于开启KeepAlive功能须要消耗额外的宽带和流量,因此TCP协议层默认并不开启KeepAlive功 能,尽管这微不足道,但在按流量计费的环境下增长了费用,另外一方面,KeepAlive设置不合理时可能会 由于短暂的网络波动而断开健康的TCP链接。而且,默认的KeepAlive超时须要7,200,000 MilliSeconds, 即2小时,探测次数为5次。对于不少服务端应用程序来讲,2小时的空闲时间太长。所以,咱们须要手工开启KeepAlive功能并设置合理的KeepAlive参数。
 
心跳检测步骤:
1客户端每隔一个时间间隔发生一个探测包给服务器
2客户端发包时启动一个超时定时器
3服务器端接收到检测包,应该回应一个包
4若是客户机收到服务器的应答包,则说明服务器正常,删除超时定时器
5若是客户端的超时定时器超时,依然没有收到应答包,则说明服务器挂了
转自:http://blog.sina.com.cn/s/blog_a459dcf5010153m5.html
 
 
 
3,http://blog.chinaunix.net/uid-26000296-id-3758651.html
TCP链接简介
当网络通讯时采用TCP协议时,在真正的读写操做以前,server与client之间必须创建一个链接,
当读写操做完成后,双方再也不须要这个链接时它们能够释放这个链接,
链接的创建是须要三次握手的,而释放则须要4次握手,
因此说每一个链接的创建都是须要资源消耗和时间消耗的
经典的三次握手示意图:


经典的四次握手关闭图:
 


1、长链接与短链接
长链接: 指在一个TCP链接上能够连续发送多个数据包,
        在TCP链接保持期间,若是没有数据包发送,须要双方发检测包以维持此链接;
        通常须要本身作在线维持。 
短链接: 指通讯双方有数据交互时,就创建一个TCP链接,数据发送完成后,则断开此TCP链接;
        通常银行都使用短链接。 
        它的优势是:管理起来比较简单,存在的链接都是有用的链接,不须要额外的控制手段 

好比http的,只是链接、请求、关闭,过程时间较短,服务器如果一段时间内没有收到请求便可关闭链接。 
其实长链接是相对于一般的短链接而说的,也就是长时间保持客户端与服务端的链接状态。

长链接与短链接的操做过程 
一般的短链接操做步骤是: 
  链接→数据传输→关闭链接;

而长链接一般就是: 
  链接→数据传输→保持链接(心跳)→数据传输→保持链接(心跳)→……→关闭链接; 

这就要求长链接在没有数据通讯时,定时发送数据包(心跳),以维持链接状态,
短链接在没有数据传输时直接关闭就好了

何时用长链接,短链接?
长链接多用于操做频繁,点对点的通信,并且链接数不能太多状况。
每一个TCP链接都须要三步握手,这须要时间,若是每一个操做都是先链接,再操做的话那么处理速度会下降不少,
因此每一个操做完后都不断开,下次次处理时直接发送数据包就OK了,不用创建TCP链接。

例如:数据库的链接用长链接, 
若是用短链接频繁的通讯会形成socket错误,并且频繁的socket 建立也是对资源的浪费。

2、发送接收方式
一、异步 
报文发送和接收是分开的,相互独立的,互不影响。这种方式又分两种状况: 
(1)异步双工:接收和发送在同一个程序中,由两个不一样的子进程分别负责发送和接收 
(2)异步单工:接收和发送是用两个不一样的程序来完成。 

二、同步 
报文发送和接收是同步进行,既报文发送后等待接收返回报文。 
同步方式通常须要考虑超时问题,即报文发出去后不能无限等待,须要设定超时时间,
超过该时间发送方再也不等待读返回报文,直接通知超时返回。
 
在长链接中通常是没有条件可以判断读写何时结束,因此必需要加长度报文头。
读函数先是读取报文头的长度,再根据这个长度去读相应长度的报文。

三. 单工、半双工和全双工
根据通讯双方的分工和信号传输方向可将通讯分为三种方式:
单工、
半双工、
全双工。

在计算机网络中主要采用双工方式,其中:
局域网采用半双工方式,
城域网和广域网采用全双年方式。   

1. 单工(Simplex)方式:
通讯双方设备中发送器与接收器分工明确,只能在由发送器向接收器的单一固定方向上传送数据。
采用单工通讯的典型发送设备如早期计算机的读卡器,典型的接收设备如打印机。   

2. 半双工(Half Duplex)方式:
通讯双方设备既是发送器,也是接收器,两台设备能够相互传送数据,但某一时刻则只能向一个方向传送数据。
例如,步话机是半双工设备,由于在一个时刻只能有一方说话。   

3. 全双工(Full Duplex)方式:
通讯双方设备既是发送器,也是接收器,两台设备能够同时在两个方向上传送数据。
例如,电话是全双工设备,由于双方可同时说话。

而像WEB网站的http服务通常都用短连接,由于长链接对于服务端来讲会耗费必定的资源,
而像WEB网站这么频繁的成千上万甚至上亿客户端的链接用短链接会更省一些资源,
若是用长链接,并且同时有成千上万的用户,若是每一个用户都占用一个链接的话,那可想而知吧。
因此并发量大,但每一个用户无需频繁操做状况下需用短连好。

总之,长链接和短链接的选择要视状况而定。

4、一个最简单的长链接与心跳保持的示例程序
/*!
 ******************************************************************************
 * \File
 *  
 * \Brief
 *   
 * \Author
 *  Hank
 ******************************************************************************
 */
  1. #include <stdio.h>
  2. #include <string.h>
  3. #include <errno.h>
  4. #include <sys/socket.h>
  5. #include <resolv.h>
  6. #include <stdlib.h>
  7. #include <netinet/in.h>
  8. #include <arpa/inet.h>
  9. #include <arpa/inet.h>
  10. #include <unistd.h>
  11. #include <sys/time.h>
  12. #include <sys/types.h>
#define MAXBUF 1024int main(int argc, char **argv){  int sockfd, len;  struct sockaddr_in dest;  char buffer[MAXBUF];  char heartbeat[20] = "hello server";  fd_set rfds;  struct timeval tv;  int retval, maxfd = -1;  if (argc != 3)  {    printf("error! the right format should be : \          \n\t\t%s IP port\n\t eg:\t%s127.0.0.1 80\n",          argv[0], argv[0]);    exit(0);  }  if ((sockfd = socket(AF_INET, SOCK_STREAM, 0)) < 0)  {    perror("Socket");    exit(errno);  }  bzero(&dest, sizeof(dest));  dest.sin_family = AF_INET;  dest.sin_port = htons(atoi(argv[2]));  memset(&(dest.sin_zero), 0, 8);  if (inet_aton(argv[1], (struct in_addr*)&dest.sin_addr.s_addr) == 0)  {    perror(argv[1]);    exit(errno);  }  if (connect(sockfd, (struct sockaddr*)&dest, sizeof(dest)) != 0)  {    perror("Connect");    exit(errno);  }  printf("\nReady to start chatting.\n\        Direct input messages and \n\        enter to send messages to the server\n");  while (1)  {    FD_ZERO(&rfds);    FD_SET(0, &rfds);    maxfd = 0;    FD_SET(sockfd, &rfds);    if (sockfd > maxfd)      maxfd = sockfd;    tv.tv_sec = 2;    tv.tv_usec = 0;    retval = select(maxfd+1, &rfds, NULL, NULL, &tv);    if (retval == -1)    {      printf("Will exit and the select is error! %s", strerror(errno));      break;    }    else if (retval == 0)    {      //printf("No message comes, no buttons, continue to wait ...\n");      len = send(sockfd, heartbeat, strlen(heartbeat), 0);      if (len < 0)      {        printf("Message '%s' failed to send ! \              The error code is %d, error message '%s'\n",              heartbeat, errno, strerror(errno));        break;      }      else      {        printf("News: %s \t send, sent a total of %d bytes!\n",              heartbeat, len);      }      continue;    }    else    {      if (FD_ISSET(sockfd, &rfds))      {        bzero(buffer, MAXBUF+1);        len = recv(sockfd, buffer, MAXBUF, 0);        if (len > 0)        {          printf("Successfully received the message: '%s',%d bytes of data\n",                  buffer, len);        }        else        {          if (len < 0)              printf("Failed to receive the message! \                    The error code is %d, error message is '%s'\n",                    errno, strerror(errno));          else              printf("Chat to terminate!\n");          break;        }      }      if (FD_ISSET(0, &rfds))      {        bzero(buffer, MAXBUF+1);        fgets(buffer, MAXBUF, stdin);        if (!strncasecmp(buffer, "quit", 4))        {          printf("Own request to terminate the chat!\n");          break;        }        len = send(sockfd, buffer, strlen(buffer)-1, 0);        if (len < 0)        {          printf("Message '%s' failed to send ! \                The error code is %d, error message '%s'\n",                buffer, errno, strerror(errno));          break;        }        else        {          printf("News: %s \t send, sent a total of %d bytes!\n",                buffer, len);        }      }    }  }  close(sockfd);  return 0;}
相关文章
相关标签/搜索