怎样解决W5200W5500在TCP通信过程中意外断开

怎样解决W5200W5500在TCP通信过程中意外断开,第1张

使用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_configc中做了定义:

void Timer_Configuration(void)

{

TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;

RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE);

TIM_TimeBaseStructureTIM_Period = 1000;

TIM_TimeBaseStructureTIM_Prescaler = 0;

TIM_TimeBaseStructureTIM_ClockDivision = TIM_CKD_DIV1;

TIM_TimeBaseStructureTIM_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_configc中对以上定义进行了初始化:

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;

心跳包的发送通常有两种技术方法。应用层自己实现的心跳包由应用程序自己发送心跳包来检测连接是否正常,大致的方法是服务器在一个Timer事件中定时向客户端发送一个短小精悍的数据包,然后启动一个低级别的线程,在该线程中不断检测客户端的回应,如果在一定时间内没有收到客户端的回应,即认为客户端已经掉线,同样,如果客户端在一定时间内没有收到服务器的心跳包,则认为连接不可用。

rmp是什么网络用词介绍如下:

RMP是Real-Time Message Push的缩写,简单来说,就是实时消息推送。在移动营销里是指通知栏这一广告形式,主要是借助明星APP实现移动互联网入口级营销,无延迟地将广告展现给目标受众。这种广告形式不依赖用户是否打开APP,用户点击广告后能够直接跳转至相应H5页面,大幅提高广告点击率和转化率。

消息推送(Push)就是通过服务器把内容主动发送到客户端的过程。运营人员通过自己的产品或第三方工具对用户移动设备进行主动消息推送。完成推送后,消息通知会展示在移动设备的锁定屏幕及通知栏上,用户点击通知即可去往相应页面。

现在流行的消息推送实现方式,主要为长链接方式实现。其原理是客户端主动和服务器建立TCP长链接,长链接建立之后,客户端定期向服务器发送心跳包用于保持链接,当有消息要发送的时候,服务器可以直接通过这个已经建立好的长链接,将消息发送到客户端。

个推作为国内移动推送领域的早期进入者,于2010年推出个推消息推送SDK产品,十余年来持续为移动开发者提供稳定、高效、智能的消息推送服务,成功服务了人民日报、新华社、CCTV、新浪微博等在内的数十万APP客户。

个推消息推送,也是运用的长链接方式实现消息推送的,其长链接稳定性高、存活好,消息送达率高。开发者通过集成个推消息推送SDK,即可简单、快捷地实现Android和iOS平台的消息推送功能,有效提高产品活跃度、增加用户留存。

socketsetKeepAlive()应该写在客户端,serveraccept()是服务器端的。

心跳的也有现成的,不过依赖别的类。大概贴一下,就是这么个意思。

public class HBHBThread extends Thread {

private CommandDeal command;

 

/

  @return the command

 /

public CommandDeal getCommand() {

return command;

}

/

  @param command

             the command to set

 /

public void setCommand(CommandDeal command) {

thiscommand = command;

}

public void run() {

long maxWait = SendConfighbhb_wait;

if (maxWait < 1000) {

maxWait = 1000;

}

Date lastTime;

long timeDiv=0;

while (1 == 1) {

 lastTime=SocketQueueObjectgetLastContactTime();

 if(lastTime!=null){

 timeDiv=(new Date())getTime() - SocketQueueObjectgetLastContactTime()getTime();

 Systemoutprintln("TimeDiv:"+timeDiv);

 }

// 上次联系的时间超过最大等待,或者还没联系过

if (lastTime == null|| timeDiv >=maxWait) {

Systemoutprintln("HBHB"); 

commandsendHbhbCommand();

}else if(maxWait>timeDiv){

maxWait=maxWait-timeDiv;

}

try{

thissleep(maxWait);

}catch(Exception e){

eprintStackTrace();

}

}

}public class HBHBCommandDeal extends CommandDealInterface {

private static Logger msg_logger = LoggergetLogger("socketMsg");

@SuppressWarnings("unchecked")

public List doCommand(SocketQueueObject socketQueueObject, Map command) throws Exception {

List<String> commandList = (List) commandget(ConstantsSMSCOMMAND);

byte[] hbhbMsg = TL1ToolcreateCommand(commandListget(0));

msg_loggerinfo(" send message to sms packed:" + new String(hbhbMsg, "ISO-8859-1"));

TL1RetrunMessage retMsg = socketQueueObjectcommNGNServer(hbhbMsg);

retMsgresolve();

msg_loggerinfo(" recevice message from sms:"+ retMsggetMessage()); 

List returnList = new ArrayList();

returnListadd(retMsg);

return returnList;

}

}

你可以自己找找看啊。

Httpd守护进程,一般都提供了keep-alive timeout时间设置参数。比如nginx的keepalive_timeout,和Apache的KeepAliveTimeout。这个keepalive_timout时间值意味着:一个http产生的tcp连接在传送完最后一个响应后,还需要hold住keepalive_timeout秒后,才开始关闭这个连接。

当httpd守护进程发送完一个响应后,理应马上主动关闭相应的tcp连接,设置 keepalive_timeout后,httpd守护进程会想说:”再等等吧,看看浏览器还有没有请求过来”,这一等,便是keepalive_timeout时间。如果守护进程在这个等待的时间里,一直没有收到浏览发过来http请求,则关闭这个http连接。

测试结果证实是后者

http keep-alive与tcp keep-alive,不是同一回事,意图不一样。http keep-alive是为了让tcp活得更久一点,以便在同一个连接上传送多个http,提高socket的效率。而tcp keep-alive是TCP的一种检测TCP[连接]状况的保鲜机制。tcp keep-alive保鲜定时器,支持三个系统内核配置参数:

echo 1800 > /proc/sys/net/ipv4/tcp_keepalive_time

echo 15 > /proc/sys/net/ipv4/tcp_keepalive_intvl

echo 5 > /proc/sys/net/ipv4/tcp_keepalive_probes

keepalive是TCP保鲜定时器,当网络两端建立了TCP连接之后,闲置idle(双方没有任何数据流发送往来)了tcp_keepalive_time后,服务器内核就会尝试向客户端发送侦测包,来判断TCP连接状况(有可能客户端崩溃、强制关闭了应用、主机不可达等等)。如果没有收到对方的回答(ack包),则会在 tcp_keepalive_intvl后再次尝试发送侦测包,直到收到对对方的ack,如果一直没有收到对方的ack,一共会尝试 tcp_keepalive_probes次,每次的间隔时间在这里分别是15s, 30s, 45s, 60s, 75s。如果尝试tcp_keepalive_probes,依然没有收到对方的ack包,则会丢弃该TCP连接。TCP连接默认闲置时间是2小时,一般设置为30分钟足够了。

使用http keep-alvie,可以减少服务端TIME_WAIT数量(因为由服务端httpd守护进程主动关闭连接)。道理很简单,相较而言,启用keep-alive,建立的tcp连接更少了,自然要被关闭的tcp连接也相应更少了。

HTTP 10中默认是关闭的,需要在http头加入"Connection: Keep-Alive",才能启用Keep-Alive;HTTP 11中默认启用Keep-Alive,如果加入"Connection: close",才关闭。

在HTTP/10版本中,并没有官方的标准来规定Keep-Alive如何工作,因此实际上它是被附加到HTTP/10协议上,如果客户端浏览器支持Keep-Alive,那么就在HTTP请求头中添加一个字段 Connection: Keep-Alive,当服务器收到附带有Connection: Keep-Alive的请求时,它也会在响应头中添加一个同样的字段来使用Keep-Alive。这样一来,客户端和服务器之间的HTTP连接就会被保持,不会断开(超过Keep-Alive规定的时间,意外断电等情况除外),当客户端发送另外一个请求时,就使用这条已经建立的连接。

从HTTP/11起,默认都开启了Keep-Alive,保持连接特性,简单地说,当一个网页打开完成后,客户端和服务器之间用于传输HTTP数据的TCP连接不会关闭,如果客户端再次访问这个服务器上的网页,会继续使用这一条已经建立的连接

Keep-Alive不会永久保持连接,它有一个保持时间,可以在不同的服务器软件(如Apache)中设定这个时间

心跳包

很多应用层协议都有HeartBeat机制,通常是客户端每隔一小段时间向服务器发送一个数据包,通知服务器自己仍然在线,并传输一些可能必要的数据。使用心跳包的典型协议是IM,比如QQ/MSN/飞信等协议。

心跳包 之所以叫心跳包是因为:它像心跳一样每隔固定时间发一次,以此来告诉服务器,这个客户端还活着。事实上这是为了保持长连接,至于这个包的内容,是没有什么特别规定的,不过一般都是很小的包,或者只包含包头的一个空包。

在TCP的机制里面,本身是存在有心跳包的机制的,也就是TCP的选项:SO_KEEPALIVE。系统默认是设置的2小时的心跳频率。但是它检查不到机器断电、网线拔出、防火墙这些断线。而且逻辑层处理断线可能也不是那么好处理。一般,如果只是用于保活还是可以的。

心跳包一般来说都是在逻辑层发送空的echo包来实现的。下一个定时器,在一定时间间隔下发送一个空包给客户端,然后客户端反馈一个同样的空包回来,服务器如果在一定时间内收不到客户端发送过来的反馈包,那就只有认定说掉线了。

其实,要判定掉线,只需要send或者recv一下,如果结果为零,则为掉线。但是,在长连接下,有可能很长一段时间都没有数据往来。理论上说,这个连接是一直保持连接的,但是实际情况中,如果中间节点出现什么故障是难以知道的。更要命的是,有的节点(防火墙)会自动把一定时间之内没有数据交互的连接给断掉。在这个时候,就需要我们的心跳包了,用于维持长连接,保活。

在获知了断线之后,服务器逻辑可能需要做一些事情,比如断线后的数据清理呀,重新连接呀……当然,这个自然是要由逻辑层根据需求去做了。

总的来说,心跳包主要也就是用于长连接的保活和断线处理。一般的应用下,判定时间在30-40秒比较不错。如果实在要求高,那就在6-9秒。

TCP协议的KeepAlive机制

学过TCP/IP的同学应该都知道,传输层的两个主要协议是UDP和TCP,其中UDP是无连接的、面向packet的,而TCP协议是有连接、面向流的协议。

所以非常容易理解,使用UDP协议的客户端(例如早期的“OICQ”,听说OICQcom这两天被抢注了来着,好古老的回忆)需要定时向服务器发送心跳包,告诉服务器自己在线。

然而,MSN和现在的QQ往往使用的是TCP连接了,尽管TCP/IP底层提供了可选的KeepAlive(ACK-ACK包)机制,但是它们也还是实现了更高层的心跳包。似乎既浪费流量又浪费CPU,有点莫名其妙。

具体查了下,TCP的KeepAlive机制是这样的,首先它貌似默认是不打开的,要用setsockopt将SOL_SOCKETSO_KEEPALIVE设置为1才是打开,并且可以设置三个参数tcp_keepalive_time/tcp_keepalive_probes/tcp_keepalive_intvl,分别表示连接闲置多久开始发keepalive的ack包、发几个ack包不回复才当对方死了、两个ack包之间间隔多长,在我测试的Ubuntu Server 1004下面默认值是7200秒(2个小时,要不要这么蛋疼啊!)、9次、75秒。于是连接就了有一个超时时间窗口,如果连接之间没有通信,这个时间窗口会逐渐减小,当它减小到零的时候,TCP协议会向对方发一个带有ACK标志的空数据包(KeepAlive探针),对方在收到ACK包以后,如果连接一切正常,应该回复一个ACK;如果连接出现错误了(例如对方重启了,连接状态丢失),则应当回复一个RST;如果对方没有回复,服务器每隔intvl的时间再发ACK,如果连续probes个包都被无视了,说明连接被断开了。

Ref:

HTTP的长连接和短连接本质上是TCP长连接和短连接。

短连接

短连接,顾名思义,与长连接的区别就是,客户端收到服务端的响应后,立刻发送FIN消息,主动释放连接。也有服务端主动断连的情况,凡是在一次消息交互(发请求-收响应)之后立刻断开连接的情况都称为短连接。

长连接/http keep-alive

也叫持久连接,即一个TCP连接服务多次请求,在TCP层握手成功后,不立即断开连接,并在此连接的基础上进行多次消息(包括心跳)交互,直至连接的任意一方(客户端OR服务端)主动断开连接,此过程称为一次完整的长连接。

在HTTP/10中得到了初步的支持,客户端在请求header中携带Connection:Keep-Alive,即是在向服务端请求持久连接。如果服务端接受持久连接,则会在响应header中同样携带Connection: Keep-Alive,这样客户端便会继续使用同一个TCP连接发送接下来的若干请求。(Keep-Alive的默认参数是[timout=5, max=100],即一个TCP连接可以服务至多5秒内的100次请求)

HTTP/11开始,即使请求header中没有携带Connection: Keep-Alive,传输也会默认以持久连接的方式进行,只有加入"Connection: close "后,才关闭。

http keepalive是客户端浏览器与服务端httpd守护进程协作的结果。

TCP中的KeepAlive

  TCP协议的实现中,提供了KeepAlive报文,用来探测连接的对端是否存活。在应用交互的过程中,可能存在以下几种情况:

客户端或服务器意外断电,死机,崩溃,重启;

中间网络已经中断,而客户端与服务器并不知道;

利用保活探测功能,可以探知这种对端的意外情况,从而保证在意外发生时,可以释放半打开的TCP连接。TCP保活报文交互过程如下:

因此,KeepAlive并不适用于检测双方存活的场景,这种场景还得依赖于应用层的心跳。 应用层心跳也具备着更大的灵活性,可以控制检测时机,间隔和处理流程,甚至可以在心跳包上附带额外信息。

应用层心跳是检测连接有效性以及判断双方是否存活的有效方式 。但是心跳过于频繁会带来 耗电和耗流量 的弊病,心跳频率过低则会影响连接检测的实时性。业内关于心跳时间的设置和优化,主要基于如下几个因素:

理想的情况下,客户端应当以略小于NAT超时时间的间隔来发送心跳包。 根据微信团队测试的一些数据,一些常用网络的NAT超时时间如下表所示:

TCP的KeepAlive机制意图在于保活、心跳,检测连接错误

如何快速区分当前连接使用的是长连接还是短连接

1、凡是在一次完整的消息交互(发请求-收响应)之后,立刻断开连接(有一方发送FIN消息)的情况都称为短连接;

2、长连接的一个明显特征是会有心跳消息(也有没有心跳的情况),且一般心跳间隔都在30S或者1MIN左右,用wireshark抓包可以看到有规律的心跳消息交互(可能会存在毫秒级别的误差)。

什么时候用长连接,短连接?

1、需要频繁交互的场景使用长连接,如即时通信工具(微信/QQ,QQ也有UDP),相反则使用短连接,比如普通的web网站,只有当浏览器发起请求时才会建立连接,服务器返回相应后,连接立即断开。

2、维持长连接会有一定的系统开销,用户量少不容易看出系统瓶颈,一旦用户量上去了,就很有可能把服务器资源(内存/CPU/网卡)耗尽,所以使用需谨慎。

keep-alive与TIME_WAIT

使用http keep-alvie,可以减少服务端TIME_WAIT数量(因为由服务端httpd守护进程主动关闭连接)。道理很简单,相较而言,启用keep-alive,建立的tcp连接更少了,自然要被关闭的tcp连接也相应更少了。

短连接和长链接 图:

参考: https://caofengbingithubio/2018/03/16/dhcp-and-nat/#4-%E5%BD%B1%E5%93%8D%E5%BF%83%E8%B7%B3%E9%A2%91%E7%8E%87%E7%9A%84%E5%85%B3%E9%94%AE%E5%9B%A0%E7%B4%A0

https://blogcsdnnet/hengyunabc/article/details/44310193

http://wingjaycom/2018/12/05/android-arch-long-link/

https://wwwlevicccom/2018/06/30/yi-dong-duan-wang-luo-you-hua/

能被我参考的都很优秀,哈哈

DABAN RP主题是一个优秀的主题,极致后台体验,无插件,集成会员系统
网站模板库 » 怎样解决W5200W5500在TCP通信过程中意外断开

0条评论

发表评论

提供最优质的资源集合

立即查看 了解详情