如何在WebSocket的服务器侧检测客户端的断开连接

如何在WebSocket的服务器侧检测客户端的断开连接,第1张

配置swoole_server->set参数:
array(
'heartbeat_idle_time' => 600,
'heartbeat_check_interval' => 60,
);

目前FScoket无法做到感知服务端关闭tcp链接,要做到只能通过两种其他方式:
1,使用BSD socket(建议使用non-blocking mode,因为这样方便终止接收线程);
2,客户端发送心跳包给服务端,如果没有得到回应则认为关闭。

你可以根据服务器收到的数据的长度来判断,如果服务器收到的数据长度是0,那么意味着你的客户端程序已经断开了连接。从TCP/IP协议栈的角度来说,就是客户端程序关闭了自己写的这一半连接,向服务器发出了一个FIN。这涉及到TCP的状态迁移,关于这方面的知识,建议你看一下Richard Stevens先生的《TCP/IP 详解》卷一和《Unix网络编程》卷一,上面有详细的解释。
关于你的第二个问题,建议你仔细看一下自己的服务器程序代码。服务器程序首先要建立一个监听socket,当有客户端连接上来时,服务器会在一个新socket上接受客户端连接。所以并不存在“乱”的问题。关于这个问题同样推荐你看上面的两本关于网络编程的经典著作。

服务器 主对话框中有一个按钮,点击按钮,进入另外一个对话框DLG1,DLG1对话框完成TCP/IP的监听,数据传输客户端 定时连接服务器定义一个全局变量uchar tcpflag=0;为0 说明没有连接,为1说明连接成功服务器程序:点击监听按钮,开始监听客户端的连接,等DLG1关闭的时候,在销毁函数中,发送一个字符串dunkai,通知客户端,服务器已经断开客户端程序:1、在定时中,判断tcpflag是否为0,如果是,说明没有连接,创建与服务器的连接,连接成功后,tcpflag=1;推出定时2、在TCP/IP接收线程中,判断接收到的数据是否为'duankai'字符串,如是,说明连接断开,令tcpflag=0;注意:在设置标志位之前要加延时

方法一:客户端主动向服务器发送信息,定时发送“我在线”,服务端定时接收信息,如果客户端发送信息的时间超过服务器容许的时间范围,判断客户端掉线(或异常退出)。
方法二:采用TCP/IP方式(question/ask),服务器主动向客户端发送信息,客户端在收到服务器信息的同时会回复“我在线”,如果没有回复信息,则判断客户端网络不或客户端掉线。

使用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;


欢迎分享,转载请注明来源:内存溢出

原文地址: http://outofmemory.cn/zz/13282734.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2023-07-07
下一篇 2023-07-07

发表评论

登录后才能评论

评论列表(0条)

保存