热度 23
2015-5-6 14:18
3034 次阅读|
0 个评论
在使用 W5200 和 W5500 的 TCP 通信过程中,有一个非常容易被问到的问题: (这里以 W5200 为例) W5200 作为服务器,假如客户端的网线断开 或 瞬间停电,服务器该怎样判断? 那么当客户端由于这些原因忽然断开,该怎样解决? 今天给大家介绍解决以上问题的办法,即如何使用 Keepalive 。 什么是 Keepalive ? Keepalive 即心跳检测,以下简称 KA ,之所以称之为心跳检测是因为它像心跳一样每隔一段时间发一次,以此来告诉对方自己是否存活。心跳检测用于 TCP 通讯过程中服务器检测客户端是处于长时间空闲(在线)还是已经断开,一般采用客户端定时发送简单的通讯包,一般是很小的包或者空包给服务器( W5200 的心跳包为 1 字节),如果在指定时间内没有收到该心跳包,则服务器会判断客户端已经断开,此时程序中的 Socket 状态机会转到 SOCKET_CLOSED 并重新打开 Socket 去连接服务器 / 监听客户端。 KeepAlive 怎么分类? KA 根据发出方不同可以分为两种,一种是由客户端发给服务器的心跳包,一种是服务器发给客户端的心跳包,选择哪一种方式需要看哪一方实现起来方便合理。需要注意的是, W5200 根据合理的设计,其心跳包需要在 Socket TCP 连接建立之后,服务器和客户端至少进行一次数据交互,且在设定的时间内没有数据交互时发出。 W5200 KA 程序说明 下面我以 W5200 的 TCP Server 官方例程为例,用 PC 建立 TCP 客户端来连接 W5200 ,说明 KA 的实现方法。 定义和初始化部分: 程序中用到了定时器和中断函数,在 w5200_config.c 中做了定义: void Timer_Configuration(void) { TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure; RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE); TIM_TimeBaseStructure.TIM_Period = 1000; TIM_TimeBaseStructure.TIM_Prescaler = 0; TIM_TimeBaseStructure.TIM_ClockDivision = TIM_CKD_DIV1; TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up; TIM_TimeBaseInit(TIM2, TIM_TimeBaseStructure); TIM_PrescalerConfig(TIM2, 71, TIM_PSCReloadMode_Immediate); TIM_Cmd(TIM2, ENABLE); TIM_ITConfig(TIM2, TIM_IT_Update, ENABLE); void Timer2_ISR(void) { ms++; // 等待时间自增,单位为 ms if((ms % 1000)==0) // 当等待时间增加到某一秒 { if(ka_tick_flag==1)ka_no_data_tick++; // 若 KA 定时器标志位为 1 ,无数据传输 时间计时器自增 if(ka_no_data_tick=NO_DATA_PERIOD) { ka_send_tick++; // 当无数据传输时间计时器值大于 NO_DATA_PERIOD , KA 发送定时器开始自增 if(ka_send_tick=KA_SEND_PERIOD) { ka_sen d_flag=1; / / 当 KA 发送定时器的大于 KA_SEND_PERIOD , KA 发送标志位置 1 ,发送一个 KA 包 } } printf("."); // 当时间没到整秒,发一个“ . ” } } 在主程序中进行初始化: Timer_Configuration(); // 定时器初始化 NVIC_Configuration(); // 中断函数初始化 程序中定义了 ka_tick_flag ( KA 定时器开始计时标志位)、 ka_send_flag ( KA 发送标志位)、 ka_no_data_tick ( KA 无数据传输时间计时器)以及 ka_send_tick ( KA 发送定时器)。在 w5200_config.c 中对以上定义进行了初始化: uint32 ka_no_data_tick=0; // 定义无数据传输时间计时器 uint8 ka_tick_flag=0; // 定义 KA 定时器开始计时标志位 uint32 ka_send_tick=0; // 定义 KA 发送定时器 uint8 ka_send_flag=0; // 定义 KA 发送标志位 主循环部分: 当程序烧录后,按 Reset 键重启 W5200 后服务器打开一个 Socket ,此时 Socket 由 SOCK_CLOSED 变为 SOCK_INIT 并处于监听状态。 PC 建立客户端成功连接 W5200 后, Socket 处于 SOCK_ESTABLISHED ,下面是程序具体的操作过程: case SOCK_ESTABLISHED: // Socket 处于连接建立状态 if(getSn_IR(0) Sn_IR_CON) { setSn_IR(0, Sn_IR_CON); // Sn_IR 的第 0 位置 1 ka_tick_flag=0; // KA 定时器开始计时标志位清零 ka_no_data_tick=0; // 无数据传输时间计时器 ka_send_flag=0; // KA 发送标志位清零 ka_send_tick=0; // KA 发送定时器清零 } if ((len = getSn_RX_RSR(0)) 0) { len = recv(0, RX_BUF, len); // W5200 收到数据并保存到 len send(0,RX_BUF,len,(bool)0); // W5200 将收到的数据发回客户端 if(ka_tick_flag==0) { ka_tick_flag=1; // W5200 同客户端进行了一次通信后,将 KA 定时器开始计时标志位置 1 ,进入定时器中断函数,只要接下来在 NO_DATA_PERIOD 内没有数据通信,就开始发 KA 包 } ka_no_data_tick=0; // 无数据传输时间计时器清零 ka_send_tick=0; // KA 发送定时器清零 } // KA 发送过程 if(ka_send_flag) { ka_send_flag=0; // KA 发送标志位清零 ka_send_tick=0; // KA 发送定时器清零 send_keepalive(0); // W5200 发 KA 包给客户端 printf("*"); / / KA 以 ”*” 为标志在串口打印出来 } break; 例程下载: 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 微信公众平台: