tcp keepalive

摘要:
在这两种情况下,client端都不会响应。服务器没有收到对其发出探测的响应,并且在一定时间后重复发送keep-alivepacket,并且重复发送一定次数。因此,我们需要手工开启Keepalive功能并设置合理的Keepalive参数。设置好keepalive以后,我们通过实验来看看当client异常退出或是网络断掉的情况下,keepalive怎么通知我们异常断开的情况。

部分信息可以看UNIX网络编程第157页,

摘录过来:

在一个正常的TCP连接上,当我们用无限等待的方式调用下面的Recv或Send的时候:

ret=recv(s,&buf[idx],nLeft,flags);

ret=send(s,&buf[idx],nLeft,flags);

如果TCP连接被对方正常关闭,也就是说,对方是正确地调用了closesocket(s)或者shutdown(s)的话,那么上面的Recv或Send调用就能马上返回,并且报错。这是由于closesocket(s)或者shutdown(s)有个正常的关闭过程,会告诉对方“TCP连接已经关闭,你不需要再发送或者接受消息了”。但是,如果是网线突然被拔掉,TCP连接的任何一端的机器突然断电或重启动,那么这时候正在执行Recv或Send操作的一方就会因为没有任何连接中断的通知而一直等待下去,也就是会被长时间卡住。这种情形解决的办法:

1>自己编写心跳包程序

简单的说也就是在自己的程序中加入一条线程,定时向对端发送数据包,查看是否有ACK,如果有则连接正常,没有的话则连接断开

2>启动TCP编程里的keepAlive机制

其实keepalive的原理就是TCP内嵌的一个心跳包,

以服务器端为例,如果当前server端检测到超过一定时间(默认是7,200,000 milliseconds,也就是2个小时)没有数据传输,那么会向client端发送一个keep-alive packet(该keep-alive packet就是ACK和当前TCP序列号减一的组合),此时client端应该为以下三种情况之一:

1. client端仍然存在,网络连接状况良好。此时client端会返回一个ACKserver端接收到ACK后重置计时器,在2小时后再发送探测。如果2小时内连接上有数据传输,那么在该时间基础上向后推延2个小时。

2. 客户端异常关闭,或是网络断开。在这两种情况下,client端都不会响应。服务器没有收到对其发出探测的响应,并且在一定时间(系统默认为1000 ms)后重复发送keep-alive packet,并且重复发送一定次数(2000 XP 2003 系统默认为5, Vista后的系统默认为10次)。

3. 客户端曾经崩溃,但已经重启。这种情况下,服务器将会收到对其存活探测的响应,但该响应是一个复位,从而引起服务器对连接的终止。

对于实用程序来说,2小时的空闲时间太长。因此,我们需要手工开启Keepalive功能并设置合理的Keepalive参数。在XP和WIN2003系统上,可以针对单独的socket来设置,但是在windows 2000,不能单独设置,如果设置,那么影响是整个系统的所有socket。

了解了keep alive大致的原理,下来看看在程序中怎么用,怎么设置参数:

#include <mstcpip.h>

		BOOL bKeepAlive = TRUE;
		int nRet = setsockopt(m_socket, SOL_SOCKET, SO_KEEPALIVE, 
		(char*)&bKeepAlive, sizeof(bKeepAlive));
		if (nRet == SOCKET_ERROR)
		{
			TRACE(L"setsockopt failed: %d\n", WSAGetLastError());
			return FALSE;
		}

		// set KeepAlive parameter
		tcp_keepalive alive_in;
		tcp_keepalive alive_out;
		alive_in.keepalivetime      = 500;  // 0.5s
		alive_in.keepaliveinterval  = 1000; //1s
		alive_in.onoff              = TRUE;

		unsigned long ulBytesReturn = 0;
		nRet = WSAIoctl(m_socket, SIO_KEEPALIVE_VALS, &alive_in, sizeof(alive_in),
		              &alive_out, sizeof(alive_out), &ulBytesReturn, NULL, NULL);
		if (nRet == SOCKET_ERROR)
		{
			TRACE(L"WSAIoctl failed: %d\n", WSAGetLastError());
			return FALSE;
		}

其中,setsockopt设置了keepalive模式,但是系统对keepalive默认的参数可能不符合我们的要求,比如空闲2小时后才探测对端是否活跃,所以WSAIoctl函数通过tcp_keepalive结构体对这些参数进行了相应设置

tcp_keepalive这个结构体在mstcpip.h头文件中有定义:

struct tcp_keepalive {
    ULONG onoff;   //是否开启keepalive
    ULONG keepalivetime;  //多长时间(ms)没有数据就开始send心跳包
ULONG keepaliveinterval; //每隔多长时间(ms)send一个心跳包,
//发5次(2000 XP 2003默认), 10次(Vista后系统默认)
};

这个结构体设置了空闲检测时间,及检测时重复发送的间隔时间

按照msdn上的说法,这些参数也可以通过在注册表里设置,分别为:

HKLM\SYSTEM\CurrentControlSet\Services\Tcpip\Parameters\KeepAliveTime

HKLM\SYSTEM\CurrentControlSet\Services\Tcpip\Parameters\KeepAliveInterval

另外,有些人可能已经发现了,tcp_keepalive这个结构体中没有对重试次数这个参数的设置,这个参数可以通过注册表来设置,具体位置为:

HKLM\SYSTEM\CurrentControlSet\Services\Tcpip\Parameters\TcpMaxDataRetransmissions

此处的keepalivetime表示的是TCP连接处于畅通时候的探测频率,一旦探测包没有返回,就以keepaliveinterval的频率发送,经过若干次的重试,如果探测包都没有返回,那么就得出结论:TCP连接已经断开,于是上面的Recv或Send调用也就能马上返回,不会无限制地卡住了。

tcp keepalive第1张

上图是对上面文字的说明。亮条之前,TCP处于畅通状态,KeepAlive是以1000毫秒(keepalivetime的值)的频率发送探测包,在发送到第32个探测包的时候,探测包没有返回,于是就以5000毫秒(keepalivetime的值)的频率发送探测包,重发几次后,探测包都没有返回,于是就得出结论:此TCP连接已经断开了!

设置好keepalive以后,我们通过实验来看看当client异常退出或是网络断掉的情况下,keepalive怎么通知我们异常断开的情况。这里采用select模式,实验环境为XP系统和Win7系统,几种情况返回值如下:

1. 正常断开

select函数正常返回,recv函数返回0

2. 异常断开

a) 程序异常退出,如client端重启,应用非正常关闭等

select函数正常返回,recv函数返回SOCKET_ERRORWSAGetLastError()得到的结果为WSAECONNRESET(10054)。

b) 网络断开

结果同上:select函数正常返回,recv函数返回SOCKET_ERRORWSAGetLastError()得到的结果为WSAECONNRESET(10054)

对于程序异常退出的情况,实际上在不开启keepalive的情况下也是可以检测到的

免责声明:文章转载自《tcp keepalive》仅用于学习参考。如对内容有疑问,请及时联系本站处理。

上篇用Python给Github的README.md做一个访客统计功能jquery 操作css 尺寸下篇

宿迁高防,2C2G15M,22元/月;香港BGP,2C5G5M,25元/月 雨云优惠码:MjYwNzM=

相关文章

C#使用SmtpClient发送邮件

原理:  例如A使用163邮箱发送邮件给B(qq邮箱)。首先A会把邮件通过SMTP(Simple Mail Transfer Protocol)协议传输到163的Smtp服务器上,163的Smtp服务器会根据B的邮箱账号,把邮件通过Smtp协议发给QQ邮箱的Smtp服务器。QQ的Smtp服务器接收到邮件消息后会将之存储在QQ邮箱的邮件存储设备上。当B登陆Q...

close函数

通常的UNIX close函数也用来关闭套接字,并终止TCP连接 #include <unistd.h> int close(int sockfd); 返回:成功返回0,出错则为-1 (1)close一个TCP套接字的默认行为是把该套接字标记成已关闭,然后立即返回调用进程,该套接字描述符不能再由调用进程使用; (2)close一个TCP...

linux 网卡buffer大小

参考截取一部分:https://blog.csdn.net/ysu108/article/details/7764461 在linux下可以修改协议栈改变tcp缓冲相关参数: 修改系统套接字缓冲区 echo 65536 > /proc/sys/net/core/rmem_maxecho 256960 > /proc/sys/net/core/w...

前端er们如何最快开发h5移动端页面?

声明在前:本文原创,一字一字手打,转载还请消息M我一下,求伸手党手下留情。   一直以来对web移动端的东西做的比较多,总是在做确从来没总结过,于是想对移动端的框架选择到相关技巧,进行一个归纳。首先声明,我也是只萌新,不是大神,肯定会有写得不够严谨的地方,欢迎指正~互相交流,若有忍不了的地方,求轻喷,轻喷,喷.....   以下为正文:   互联网已经进入...

使用wireshark抓包分析SOCKS5协议

目录 编写SOCKS5服务器运行代码(参考自Python编写socks5服务器) 使用SOCKS5服务器脚本和curl命令 分析抓取到的数据包理解SOCKS5协议的工作过程(感谢socks5代理服务器协议的说明让我预先知道SOCKS5协议数据消息传递的机理) 通信软件课选择了分析SOCKS5协议,想看一下这个协议在网络通信中是如何进行的,遂抓包...

重新安装 tcp/ip协议

环境 windows xp 故障描述:能ping 通 ip,但 ping 不通 域名。 解决办法:重新安装 tcp/ip协议。 1.打开注册表删除以下键值HKEY_LOCAL_MACHINE\System\GurrentrolSet\Services\Winsock      HKEY_LOCAL_MACHINE\System\GurrentrolSet\...