怎样解决W5200/W5500在TCP通信过程中意外断开?(Keepalive)

Home / 博客 / 怎样解决W5200/W5500在TCP通信过程中意外断开?(Keepalive)

在使用W5200和W5500的TCP通信过程中,有一个非常容易被问到的问题:

(这里以W5200为例)

W5200作为服务器,假如客户端的网线断开 或 瞬间停电,服务器该怎样判断?

那么当客户端由于这些原因忽然断开,该怎样解决?

今天给大家介绍解决以上问题的办法,即如何使用Keepalive。

 

什么是Keepalive?

Keepalive即心跳检测,以下简称KA,之所以称之为心跳检测是因为它像心跳一样每隔一段时间发一次,以此来告诉对方自己是否存活。心跳检测用于TCP通讯过程中服务器检测客户端是处于长时间空闲(在线)还是已经断开,一般采用客户端定时发送简单的通讯包,一般是很小的包或者空包给服务器(W5200的心跳包为1字节),如果在指定时间内没有收到该心跳包,则服务器会判断客户端已经断开,此时程序中的Socket状态机会转到SOCKET_CLOSED并重新打开Socket去连接服务器/监听客户端。

KeepAlive怎么分类?

       KA根据发出方不同可以分为两种,一种是由客户端发给服务器的心跳包,一种是服务器发给客户端的心跳包,选择哪一种方式需要看哪一方实现起来方便合理。需要注意的是,W5200根据合理的设计,其心跳包需要在Socket TCP连接建立之后,服务器和客户端至少进行一次数据交互,且在设定的时间内没有数据交互时发出。

W5200 KA程序说明

       下面我以W5200TCP Server官方例程为例,用PC建立TCP客户端来连接W5200,说明KA的实现方法。

定义和初始化部分:

程序中用到了定时器和中断函数,在w5200_config.c中做了定义:

文本框: void Timer_Configuration(void)<br /><br /><br /><br /><br /><br /><br /><br /><br /><br />
{<br /><br /><br /><br /><br /><br /><br /><br /><br /><br />
  TIM_TimeBaseInitTypeDef  TIM_TimeBaseStructure;<br /><br /><br /><br /><br /><br /><br /><br /><br /><br />
  RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE);<br /><br /><br /><br /><br /><br /><br /><br /><br /><br />
  TIM_TimeBaseStructure.TIM_Period = 1000;<br /><br /><br /><br /><br /><br /><br /><br /><br /><br />
  TIM_TimeBaseStructure.TIM_Prescaler = 0;<br /><br /><br /><br /><br /><br /><br /><br /><br /><br />
  TIM_TimeBaseStructure.TIM_ClockDivision = TIM_CKD_DIV1;<br /><br /><br /><br /><br /><br /><br /><br /><br /><br />
  TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;<br /><br /><br /><br /><br /><br /><br /><br /><br /><br />
  TIM_TimeBaseInit(TIM2, &TIM_TimeBaseStructure);<br /><br /><br /><br /><br /><br /><br /><br /><br /><br />
TIM_PrescalerConfig(TIM2, 71, TIM_PSCReloadMode_Immediate);<br /><br /><br /><br /><br /><br /><br /><br /><br /><br />
TIM_Cmd(TIM2, ENABLE);<br /><br /><br /><br /><br /><br /><br /><br /><br /><br />
  TIM_ITConfig(TIM2, TIM_IT_Update, ENABLE);<br /><br /><br /><br /><br /><br /><br /><br /><br /><br />
}<br /><br /><br /><br /><br /><br /><br /><br /><br /><br />
文本框: void Timer2_ISR(void)<br /><br /><br /><br /><br /><br /><br /><br /><br /><br />
{<br /><br /><br /><br /><br /><br /><br /><br /><br /><br />
  ms++;										// 等待时间自增,单位为ms<br /><br /><br /><br /><br /><br /><br /><br /><br /><br />
  if((ms % 1000)==0)							// 当等待时间增加到某一秒<br /><br /><br /><br /><br /><br /><br /><br /><br /><br />
  {<br /><br /><br /><br /><br /><br /><br /><br /><br /><br />
    if(ka_tick_flag==1)ka_no_data_tick++;		// 若KA定时器标志位为1,无数据传输<br /><br /><br /><br /><br /><br /><br /><br /><br /><br />
时间计时器自增<br /><br /><br /><br /><br /><br /><br /><br /><br /><br />
    if(ka_no_data_tick>=NO_DATA_PERIOD)<br /><br /><br /><br /><br /><br /><br /><br /><br /><br />
    {<br /><br /><br /><br /><br /><br /><br /><br /><br /><br />
   ka_send_tick++;			// 当无数据传输时间计时器值大于<br /><br /><br /><br /><br /><br /><br /><br /><br /><br />
NO_DATA_PERIOD,KA发送定时器开始自增<br /><br /><br /><br /><br /><br /><br /><br /><br /><br />
      if(ka_send_tick>=KA_SEND_PERIOD)<br /><br /><br /><br /><br /><br /><br /><br /><br /><br />
      {<br /><br /><br /><br /><br /><br /><br /><br /><br /><br />
        ka_send_flag=1;					// 当KA发送定时器的大于<br /><br /><br /><br /><br /><br /><br /><br /><br /><br />
KA_SEND_PERIOD,KA发送标志位置1,发送一个KA包<br /><br /><br /><br /><br /><br /><br /><br /><br /><br />
      }<br /><br /><br /><br /><br /><br /><br /><br /><br /><br />
    }<br /><br /><br /><br /><br /><br /><br /><br /><br /><br />
    printf(".");								// 当时间没到整秒,发一个“.”<br /><br /><br /><br /><br /><br /><br /><br /><br /><br />
  }<br /><br /><br /><br /><br /><br /><br /><br /><br /><br />
}<br /><br /><br /><br /><br /><br /><br /><br /><br /><br />

在主程序中进行初始化:

文本框: Timer_Configuration();			// 定时器初始化<br /><br /><br /><br /><br /><br /><br /><br /><br /><br />
NVIC_Configuration(); 			// 中断函数初始化<br /><br /><br /><br /><br /><br /><br /><br /><br /><br />

程序中定义了ka_tick_flagKA定时器开始计时标志位)、ka_send_flagKA发送标志位)、ka_no_data_tickKA无数据传输时间计时器)以及ka_send_tickKA发送定时器)。在w5200_config.c中对以上定义进行了初始化:

文本框: uint32 ka_no_data_tick=0;				// 定义无数据传输时间计时器<br /><br /><br /><br /><br /><br /><br /><br /><br /><br />
uint8 ka_tick_flag=0;						// 定义KA定时器开始计时标志位<br /><br /><br /><br /><br /><br /><br /><br /><br /><br />
uint32 ka_send_tick=0;					// 定义KA发送定时器<br /><br /><br /><br /><br /><br /><br /><br /><br /><br />
uint8 ka_send_flag=0;					// 定义KA发送标志位<br /><br /><br /><br /><br /><br /><br /><br /><br /><br />

 

主循环部分:

当程序烧录后,按Reset键重启W5200后服务器打开一个Socket,此时SocketSOCK_CLOSED变为SOCK_INIT并处于监听状态。PC建立客户端成功连接W5200后,Socket处于SOCK_ESTABLISHED,下面是程序具体的操作过程:

文本框: case SOCK_ESTABLISHED:							// Socket处于连接建立状态<br /><br /><br /><br /><br /><br /><br /><br /><br /><br />
if(getSn_IR(0) & Sn_IR_CON)<br /><br /><br /><br /><br /><br /><br /><br /><br /><br />
	{<br /><br /><br /><br /><br /><br /><br /><br /><br /><br />
	  setSn_IR(0, Sn_IR_CON);					// Sn_IR的第0位置1<br /><br /><br /><br /><br /><br /><br /><br /><br /><br />
	  ka_tick_flag=0;							// KA定时器开始计时标志位清零<br /><br /><br /><br /><br /><br /><br /><br /><br /><br />
	  ka_no_data_tick=0;						// 无数据传输时间计时器<br /><br /><br /><br /><br /><br /><br /><br /><br /><br />
	  ka_send_flag=0;							// KA发送标志位清零<br /><br /><br /><br /><br /><br /><br /><br /><br /><br />
	  ka_send_tick=0;							// KA发送定时器清零<br /><br /><br /><br /><br /><br /><br /><br /><br /><br />
	}<br /><br /><br /><br /><br /><br /><br /><br /><br /><br />
	if ((len = getSn_RX_RSR(0)) > 0)<br /><br /><br /><br /><br /><br /><br /><br /><br /><br />
	{<br /><br /><br /><br /><br /><br /><br /><br /><br /><br />
	  len = recv(0, RX_BUF, len);					// W5200收到数据并保存到len<br /><br /><br /><br /><br /><br /><br /><br /><br /><br />
	  send(0,RX_BUF,len,(bool)0);				// W5200将收到的数据发回客户端<br /><br /><br /><br /><br /><br /><br /><br /><br /><br />
	  if(ka_tick_flag==0)<br /><br /><br /><br /><br /><br /><br /><br /><br /><br />
	  {<br /><br /><br /><br /><br /><br /><br /><br /><br /><br />
	  	ka_tick_flag=1;							// W5200同客户端进行了一次通信后,将KA定时器开始计时标志位置1,进入定时器中断函数,只要接下来在NO_DATA_PERIOD内没有数据通信,就开始发KA包<br /><br /><br /><br /><br /><br /><br /><br /><br /><br />
	   }<br /><br /><br /><br /><br /><br /><br /><br /><br /><br />
	    ka_no_data_tick=0;						// 无数据传输时间计时器清零<br /><br /><br /><br /><br /><br /><br /><br /><br /><br />
	    ka_send_tick=0;							// KA发送定时器清零<br /><br /><br /><br /><br /><br /><br /><br /><br /><br />
	  }<br /><br /><br /><br /><br /><br /><br /><br /><br /><br />
	// KA发送过程<br /><br /><br /><br /><br /><br /><br /><br /><br /><br />
if(ka_send_flag)<br /><br /><br /><br /><br /><br /><br /><br /><br /><br />
	{<br /><br /><br /><br /><br /><br /><br /><br /><br /><br />
	  ka_send_flag=0;							// KA发送标志位清零<br /><br /><br /><br /><br /><br /><br /><br /><br /><br />
	  ka_send_tick=0;							// KA发送定时器清零<br /><br /><br /><br /><br /><br /><br /><br /><br /><br />
	  send_keepalive(0);							// W5200发KA包给客户端<br /><br /><br /><br /><br /><br /><br /><br /><br /><br />
	  printf("*");								// KA以”*”为标志在串口打印出来<br /><br /><br /><br /><br /><br /><br /><br /><br /><br />
	}<br /><br /><br /><br /><br /><br /><br /><br /><br /><br />
	break;<br /><br /><br /><br /><br /><br /><br /><br /><br /><br />
例程下载:W5200: http://pan.baidu.com/s/1eQ3vkZo

W5500: http://pan.baidu.com/s/1sj7ILBn

感谢阅读!

欢迎访问:

WIZnet官方网站:http://www.iwiznet.co.kr

WIZnet官方微博:http://weibo.com/wiznet2012

WIZnet微信公众平台:

WIZnet_wechat