怎樣解決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