基于Windows Socket 的网络通信中的心跳机制原理
引言
在采用TCP 连接的C/S 结构的系统中,当通信的一方正常关闭或退出时,另一方能收到相应的连接
断开的通知,然后进行必要的处理;但如果任意一方发生所谓的“非优雅断开”,如:意外崩溃、死机、
拔掉网线或路由器故障时,另一方无法得知TCP 连接已经失效,除非继续在此连接上不断地发送数据,
经过若干时间后导致错误返回。但在很多时候,更希望服务器端和客户端都能及时有效地检测到网络连
接的非正常断开,然后完成一些必要的清理工作并把错误报告给用户。
如何及时有效地检测到通信一方的非正常断开,采用的方法是通过通信的一方或双方发送心跳包来
告诉对方网络通信是否正常或已断开。
1. 心跳原理
在基于电路交换的网络中,有专用的控制信令通道,能够及时发现通路断开、故障,而TCP/IP 网络
中,链路的连通只在连接双方记录状态,物理通道内不存在一个实际的连接链路,通信的双方只能定时
发送简单的信息给另一方,并根据超时来判断线路是长时间空闲还是已断开。这种通过每隔一定时间发
送一个固定信息给对方,对方收到后回复一个固定信息,告诉对方“我还在”的方式非常类似于心跳,所
发送的这种简单信息就称为“心跳包”。
心跳包的发送,通常有两种技术:一种是由用户在应用层实现的心跳包,另一种是由TCP 协议层提
供的KeepAlive 。
2. 应用层自己实现的心跳包
由应用程序自己发送心跳包来检测连接是否正常,大致的方法是:服务器在一个Timer 事件中定时
向客户端发送一个短小精悍的数据包,然后启动一个低级别的线程,在该线程中不断检测客户端的回应,
如果在一定时间内没有收到客户端的回应,即认为客户端已经掉线;同样,如果客户端在一定时间内没
有收到服务器的心跳包,则认为连接不可用。
3. TCP 的KeepAlive 保活机制
因为要考虑到一个服务器通常会连接多个客户端,因此由用户在应用层自己实现心跳包,代码较多
且稍显复杂,而利用TCP/IP 协议层为内置的KeepAlive 功能来实现心跳功能则简单得多。
不论是服务端还是客户端,一方开启KeepAlive 功能后,就会自动在规定时间内向对方发送心跳包,
而另一方在收到心跳包后就会自动回复,以告诉对方我仍然在线。
因为开启KeepAlive 功能需要消耗额外的宽带和流量,所以TCP 协议层默认并不开启KeepAlive 功
能,尽管这微不足道,但在按流量计费的环境下增加了费用,另一方面,KeepAlive 设置不合理时可能会
因为短暂的网络波动而断开健康的TCP 连接。并且,默认的KeepAlive 超时需要7,200,000 MilliSeconds,
即2 小时,探测次数为5 次。对于很多服务端应用程序来说,2 小时的空闲时间太长。因此,我们需要手
工开启KeepAlive 功能并设置合理的KeepAlive 参数。
目前主要有三种方法来实现用户掉线检测:SO_KEEPALIVE ,SIO_KEEPALIVE_VALS 和Heart-Beat线程。
下面我就上面的三种方法来做一下介绍。
(1)SO_KEEPALIVE 机制
这是socket库提供的功能,设置接口是setsockopt API:
BOOL bSet=TRUE;
setsockopt(hSocket,SOL_SOCKET,SO_KEEPALIVE,(const char*)&bSet,sizeof(BOOL));
根据MSDN的文档,如果为socket设置了KEEPALIVE选项,TCP/IP栈在检测到对方掉线后,
任何在该socket上进行的调用(发送/接受调用)就会立刻返回,错误号是WSAENETRESET ;
同时,此后的任何在该socket句柄的调用会立刻失败,并返回WSAENOTCONN错误。
该机制的缺点也很明显:
默认设置是空闲2小时才发送一个“保持存活探测分节”,不能保证实时检测!
当然也可以修改时间间隔参数,但是会影响到所有打开此选项的套接口!
关联了完成端口的socket可能会忽略掉该套接字选项。
(2)SIO_KEEPALIVE_VALS 机制
这是从彭博兄那里学到一个机制拉,设置接口是WSAIoctl API:
DWORD dwError = 0L ;
tcp_keepalive sKA_Settings = {0}, sReturned = {0} ;
sKA_Settings.onoff = 1 ;
sKA_Settings.keepalivetime = 5500 ; // Keep Alive in 5.5 sec.
sKA_Settings.keepaliveinterval = 3000 ; // Resend if No-Reply
if (WSAIoctl(skNewConnection, SIO_KEEPALIVE_VALS, &sKA_Settings,
sizeof(sKA_Settings), &sReturned, sizeof(sReturned), &dwBytes,
NULL, NULL) != 0)
{
dwError = WSAGetLastError() ;
}
实现时需要添加tcp_keepalive and SIO_KEEPALIVE_VALS的定义文件MSTCPiP.h
该选项不同于SO_KEEPALIVE 机制的就是它是针对单个连接的,对系统其他的套接
口并不影响。
针对完成端口的socket,设置了SIO_KEEPALIVE_VALS后,激活包由TCP STACK来负责。
当网络连接断开后,TCP STACK并不主动告诉上层的应用程序,但是当下一次RECV或者SEND操作
进行后,马上就会返回错误告诉上层这个连接已经断开了.如果检测到断开的时候,在这个连接
上有正在PENDING的IO操作,则马上会失败返回.
该机制的缺点:
不通用啦。MS的API只能用于Windows拉。不过,呵呵用彭博兄的评论就是:
优雅一些^_^.
(3)Heart-Beat线程
没说的。自己写一个后台线程,实现Heart-Beat包,客户端受到该包后,立刻返回相应的反馈 包。
该方法的好处是通用,但缺点就是会改变现有的通讯协议!
有开发网络应用经历的人都知道,网络中的接收和发送数据都是使用WINDOWS中的SOCKET进行实现。但是如果此套接字已经断开,那发送数据和接收数据的时候就一定会有问题。可是如何判断这个套接字是否还可以使用呢?
有人一定想到使用Send函数中的返回结果来进行判断。如果返回的长度和自己发送出去的长度一致,那就说明这个套接字是可用的,否则此套接字一定出现了问题。但是我们并不是无时无刻的发送数据呀。如何解决呢?
其实TCP中已经为我们实现了一个叫做心跳的机制。如果你设置了心跳,那TCP就会在一定的时间(比如你设置的是3秒钟)内发送你设置的次数的心跳(比如说2次),并且此信息不会影响你自己定义的协议。
在VC中实现心跳的例子很多,可是在DLEPHI中一直没有相应的代码。下面我是我使用DELPHI编写的关于心跳的代码(以IOCP为例),希望对大家有帮助。
定义心跳常量
const
IOC_IN =$80000000;
IOC_VENDOR =$18000000;
IOC_out =$40000000;
SIO_KEEPALIVE_VALS =IOC_IN or IOC_VENDOR or 4;
var
inKeepAlive,OutKeepAlive:TTCP_KEEPALIVE;
实现代码是在Acceptsc:= WSAAccept(Listensc, nil, nil, nil, 0);代码的后面加入:
opt:=1;
if setsockopt(Acceptsc,SOL_SOCKET,SO_KEEPALIVE,@opt,sizeof(opt))=SOCKET_ERROR then
begin
closesocket(Acceptsc);
end;
inKeepAlive.onoff:=1;
//设置3秒钟时间间隔
inKeepAlive.keepalivetime:=3000;
//设置每3秒中发送1次的心跳
inKeepAlive.keepaliveinterval:=1;
insize:=sizeof(TTCP_KEEPALIVE);
outsize:=sizeof(TTCP_KEEPALIVE);
if WSAIoctl(Accept,SIO_KEEPALIVE_VALS,@inKeepAlive,insize,@outKeepAlive,outsize,@outByte,nil,nil)=SOCKET_ERROR then
begin
closesocket(Acceptsc);
end;
如果加入以上的代码以后,系统会每3秒中加入一次的心跳。并且如果客户端断线以后(网线断),函数GetQueuedCompletionStatus会返回FALSE。
if (GetQueuedCompletionStatus(CompletionPort, BytesTransferred,DWORD(PerHandleData), POverlapped(PerIoData), INFINITE) = False) then
begin
//在这里处理客户端断线信息。
continue;
end;
以上就是我使用心跳的方法,此方法我已经在我的网络游戏中使用。情况稳定!
网络程序容易出现死连接,连接以经无效了,但是连接状态还是ESTABLISHED状态,如何识别连接以经失效就需要使用
------------------------------------------------------------------------------------------------------
下面是方法2的一段可用代码(经过僧僧鞋子 测试 可用!!!)
---------------------------------------------------------------------------------------
保活机制(心跳机制)下面是一个代码效果还是挺好的,感谢cooldiyer的资料
typedef struct STcpKeepAlive2
{
DWORD onoff;
DWORD keepalivetime;
DWORD keepaliveinterval;
}TCP_KEEP_ALIVE2;
VOID SetKeepLive(SOCKET m_Socket)
{
const char chOpt = 1; // True
// Set KeepAlive 开启保活机制, 防止服务端产生死连接
if (setsockopt(m_Socket, SOL_SOCKET, SO_KEEPALIVE, (char *)&chOpt, sizeof(chOpt)) == 0)
{
// 设置超时详细信息
TCP_KEEP_ALIVE2 klive;
klive.onoff = 1; // 启用保活
klive.keepalivetime = 1000 * 15; // 3分钟超时 Keep Alive
klive.keepaliveinterval = 1000 * 5; // 重试间隔为5秒 Resend if No-Reply
WSAIoctl
(
m_Socket,
SIO_KEEPALIVE_VALS,
&klive,
sizeof(TCP_KEEP_ALIVE2),
NULL,
0,
(unsigned long *)&chOpt,
0,
NULL
);
}
}
其中SIO_KEEPALIVE_VALS的值定义为: const int SIO_KEEPALIVE_VALS =IOC_IN | IOC_VENDOR | 4;
注:SIO_KEEPALIVE_VALS 只有在winsock2下才可使用
基于Windows Socket 的网络通信中的心跳机制原理相关推荐
- 这样讲 Netty 中的心跳机制,还有谁不会?
作者:永顺 segmentfault.com/a/1190000006931568 基础 何为心跳 顾名思义, 所谓 心跳, 即在 TCP 长连接中, 客户端和服务器之间定期发送的一种特殊的数据包, ...
- Netty 中的心跳机制
何为心跳 顾名思义, 所谓 心跳, 即在 TCP 长连接中, 客户端和服务器之间定期发送的一种特殊的数据包, 通知对方自己还在线, 以确保 TCP 连接的有效性. 为什么需要心跳 因为网络的不可靠性, ...
- Android平台基于asmack实现XMPP协议中的PubSub机制
Android平台基于asmack实现XMPP协议中的PubSub机制 本文主要介绍,在Android平台上基于asmack包,实现的PubSub机制,在PubSub中最重要的概念是节点Node,几乎 ...
- Eureka中的心跳机制
前言 从以前的单体架构到现在的微服务分布式架构, 随着架构的演变, 所需要的技术越来越多, 要求的也越来越多了, 今天来谈一下微服务领域中的心跳机制 在微服务领域,心跳机制很常见了, ...
- 聊聊分布式存储系统中的心跳机制以及主节点下发指令给从节点
心跳( heartbeat )是分布式系统中常用的技术.顾名思义,心跳就是以固定的频率向其他节点汇报当前节点状态的方式.收到心跳,一般可以认为发送心跳的这个节点在当前的网络中状态是良好的. 同时分布式 ...
- vue 心跳监控_【笔记】vue中websocket心跳机制
data () { return { ws: null,//建立的连接 lockReconnect: false,//是否真正建立连接 timeout: 28*1000,//30秒一次心跳 timeo ...
- Qt中消息的机制原理
参考文章:https://blog.csdn.net/perfectguyipeng/article/details/78082360 原理基础介绍 Qt中通过object类定义了connect函数, ...
- php 如何实现心跳包,Socket心跳机制-JS+PHP实现
本文是我在实际工作中用到的Socket通信,关于心跳机制的维护方式,特意总结了一下,希望对朋友们有所帮助. Socket应用:首先Socket 封装了tcp协议的,通过长连接的方式来与服务器通信,是由 ...
- socket心跳机制图片_socket心跳包机制
心跳包 心跳包就是在客户端和服务器间定时通知对方自己状态的一个自己定义的命令字,按照一定的时间间隔发送,类似于心跳,所以叫做心跳包 心跳包的作用 网络中的接收和发送数据都是使用SOCKET进行实现.但 ...
最新文章
- 基于GAN的图像水印去除器,效果堪比PS高手
- mac下androidStudio 运行模拟器出现:
- matlab imdilate_MATLAB--数字图像处理 数学形态学应用
- 压缩可以卸载吗_番禺街坊注意!微信发送高清大文件不压缩,网友:QQ可以卸载了?...
- 机器人定位导航技术 激光SLAM与视觉SLAM谁更胜一筹?
- ckfinder 配置 php,CKEditor4+CKFinder3(php版本)安装及配置方法
- 记录在训练深度学习算法过程中遇到的问题及解决办法。持续更新中....
- js实现拼图游戏(数字版本与图片版本)
- 一站解决:如何用cd-hit去低于30%的冗余(资源见百度云链接)
- 降本提效,贝壳搜索推荐架构统一之路
- Unity选择外部图片设置头像封面功能
- 【强化学习论文合集】三十一.2021智能体和多智能体系统国际联合会议论文(AAMAS2021)
- 如何更改超级用户密码
- Google Earth 常用操作
- 2023-2028年中国硫氰酸胍(GTC)市场研究及前景投资预测报告
- (附源码)springboot外委员工后台管理系统 毕业设计101157
- Spring Boot核心原理-自动配置
- electron 连接打印机打印pdf文件
- 让eclipse调试和豌豆荚并存
- 科学计算机有趣玩法,简单有趣!7个在家玩的趣味科学实验游戏!
热门文章
- 数据库-聚合函数-max函数
- 入门程序之入门代码编写
- 使用Github(目的、基本你概念、注册账号)
- add-apt-repository:找不到命令_嵌入式开发之交叉编译程序万能命令_以freetype为例...
- redirect通过url_for传递参数_Excel VBA解读 | 进阶篇(127):Sub过程的参数传递技术...
- dicom格式怎么转换_QQ音乐下载的歌曲怎么转换MP3?音频转换格式详细教程
- python两个数相加时_怎么用python让两个小数相加
- 未发先侃?对比华为,高通第二代5G调制解调器如何?
- 七个你没用过的炫酷开发工具推荐
- Java(第十五章)