概述
目录
1 TCP保活的必要性
2 导致TCP断连的因素
3 保活的两种方式
3.1 应用层面的心跳机制
3.2 TCP协议自带的保活功能
4 两种方式的优劣性
5 到底选用那种心跳方式?
6 配置 Keep-alive
6.1 socket 进行单独设定keepalive(代码层面)
6.2 修改系统配置文件
7 测试结果
1 TCP保活的必要性
- 多防火墙等对于空闲socket自动关闭
- 对于非正常断开, 服务器并不能检测到. 为了回收资源, 必须提供一种检测机制.
2 导致TCP断连的因素
如果网络正常, socket也通过close操作来进行优雅的关闭, 那么一切完美. 可是有很多情况, 比如网线故障, 客户端一侧突然断电或者崩溃等等, 这些情况server并不能正常检测到连接的断开.
3 保活的两种方式
3.1 应用层面的心跳机制
自定义心跳消息头. 一般客户端主动发送, 服务器接收后进行回应(也可以不回应). 这里不进行详述.
PS: 有人从软件的功能角度列出第三种方式, 就是通过第三方软件来进行探测, 确定连接的有效性. 这种方式局限性很大, 而且不属于软件内部的功能实现. 不进行讨论.
3.2 TCP协议自带的保活功能
打开keep-alive功能即可. 具体属性也可以通过API设定.
4 两种方式的优劣性
TCP协议自带的保活功能, 使用起来简单, 减少了应用层代码的复杂度. 推测也会更节省流量, 因为一般来说应用层的数据传输到协议层时都会被加上额外的包头包尾. 由TCP协议提供的检活, 其发的探测包, 理论上实现的会更精妙(用更少的字节完成更多的目标), 耗费更少的流量.
由应用自己实现的应用层的心跳, 为心跳消息额外定义一个消息类型就可以了. 就是应用正常的消息包, 只是这个包特殊点, 专门用来检活而已, 通常比较小, 可能只有消息头就可以了, 除非需要额外的信息.
应用层心跳的好处我个人的理解有两点:
一是比较灵活, 因为协议层的心跳只能提供最纯粹的检活功能, 但是应用层自己可以随意控制, 包括协议可能提供的是秒级的, 但是你想做成毫秒级的都任意(虽然实际几乎不会有这种时间级别的心跳), 包里还甚至可以携带额外的信息, 这些都是灵活之处.
二是通用, 应用层的心跳不依赖协议. 如果有一天不用TCP要改为UDP了, 协议层不提供心跳机制了, 但是你应用层的心跳依旧是通用的, 可能只需要做少许改动就可以继续使用.
应用层心跳的不好的地方也很显而易见, 增加开发工作量, 由于应用特定的网络框架, 还可能很增加代码结构的复杂度. 再就是根据上面的推测, 应用层心跳的流量消耗还是更大的, 毕竟这本质上还是个普通的数据包.
5 到底选用那种心跳方式?
优劣点第4节已经进行了阐述, 因此如果能确定你们更换协议的可能性非常小, 同时只是需要检活的功能, 那么用协议自带的就绝对OK了, 使用简单而且高效. 有些自负的人总喜欢用自己搞的, 来代替成熟协议自带的东西, 代替系统内核提供的东西, 其实往往你应用层实现的东西, 都是更拙劣的. 网上看了一些关于协议的Keep-alive不靠谱的说法, 也都比较空想和想当然, 都没有拿出任何事实论据或实验数据. 这点大家有见解, 欢迎交流哈~
6 配置 Keep-alive
keepalive默认是关闭的, 因为虽然流量极小, 毕竟是开销. 因此需要用户手动开启. 有两种方式开启.
6.1 socket 进行单独设定keepalive(代码层面)
#include <netinet/tcp.h> //for SOL_TCP和TCP_KEEPIDLE等宏
int socket_set_keepalive( int fd)
{
int alive, idle, cnt, intv;
/* Set: use keepalive on fd */
//缺省值:0(关闭),之前忘了开了,始终未看到现象
alive = 1;
if (setsockopt (fd, SOL_SOCKET, SO_KEEPALIVE, &alive, sizeof alive) != 0)
{
printf ("Set keepalive error: %s.n" , strerror (errno));
return -1;
}
/* 10秒钟无数据,触发保活机制,发送保活包 */
idle = 10;
if (setsockopt(fd, SOL_TCP, TCP_KEEPIDLE , &idle, sizeof idle) != 0)
{
printf ("Set keepalive idle error: %s.n" , strerror (errno));
return -1;
}
/* 如果没有收到回应,则3秒钟后重发保活包 */
intv = 3;
if (setsockopt(fd, SOL_TCP, TCP_KEEPINTVL , &intv, sizeof intv) != 0)
{
printf ("Set keepalive intv error: %s.n", strerror (errno));
return -1;
}
/* 连续5次没收到保活包,视为连接失效,此时本端会发送RST关闭连接 */
cnt = 5;
if (setsockopt(fd, SOL_TCP, TCP_KEEPCNT , &cnt, sizeof cnt) != 0)
{
printf ("Set keepalive cnt error: %s.n", strerror (errno));
return -1;
}
return 0;
}
6.2 修改系统配置文件
该配置文件对整个系统所有的socket有效,我们可以用cat命令查看到系统中这几个默认的值.
#cat /proc/sys/net/ipv4/tcp_keepalive_time 7200
#cat /proc/sys/net/ipv4/tcp_keepalive_intvl 75
#cat /proc/sys/net/ipv4/tcp_keepalive_probes 9
==================================================
#echo 10 > /proc/sys/net/ipv4/tcp_keepalive_time
#echo 5 > /proc/sys/net/ipv4/tcp_keepalive_probes
#echo 3 > /proc/sys/net/ipv4/tcp_keepalive_intvl
7 测试结果
- 客户端调用 socket_set_keepalive()接口配置keepalive参数,并使能keepalive
- 客户端建链完成之后向服务器发送一包数据后睡眠
- 服务器收包客户端端的一包数据之后也进入睡眠,此时通信双方再也没有数据传输
- 客户端keepalvie超时开始发送心跳包,收到服务器响应,每隔tcp_keepalive_time时长发送一次
- 配置防火墙丢掉客户端的报文后,客户端开始每隔tcp_keepalive_intvl时长发送一次,发送tcp_keepalive_probes次
- 在tcp_keepalive_probes次数均无响应之后,客户端发送RST报文关闭连接
服务器配置防火墙,丢掉客户端过来的报文
[root@localhost wq]# iptables -t filter -A INPUT -s 10.228.90.11/32 -p tcp --sport 8002 -j DROP
最后
以上就是细心钢笔为你收集整理的linux内核协议栈 TCP连接探测中的Keepalive和心跳包使用1 TCP保活的必要性2 导致TCP断连的因素3 保活的两种方式4 两种方式的优劣性5 到底选用那种心跳方式?6 配置 Keep-alive7 测试结果的全部内容,希望文章能够帮你解决linux内核协议栈 TCP连接探测中的Keepalive和心跳包使用1 TCP保活的必要性2 导致TCP断连的因素3 保活的两种方式4 两种方式的优劣性5 到底选用那种心跳方式?6 配置 Keep-alive7 测试结果所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
发表评论 取消回复