网络编程知识
TCP Keepalive
长连接的环境下,人们一般使用业务层面或上层应用层协议(诸如MQTT,SOCKET.IO等)里面定义和使用。
一旦有热数据需要传递,若此时连接已经被中介设备断开,应用程序没有及时感知的话,那么就会导致在一个无效的数据链路层面发送业务数据,结果就是发送失败。
无论是因为客户端意外断电、死机、崩溃、重启,还是中间路由网络无故断开、NAT超时等,服务器端要做到快速感知失败,减少无效链接操作。
协议解读
下面协议解读,基于RFC1122#TCP Keep-Alives。
- TCP Keepalive虽不是标准规范,但操作系统一旦实现,默认情况下须为关闭,可以被上层应用开启和关闭。
- TCP Keepalive必须在没有任何数据(包括ACK包)接收之后的周期内才会被发送,允许配置,默认值不能够小于2个小时
- 不包含数据的ACK段在被TCP发送时没有可靠性保证,意即一旦发送,不确保一定发送成功。系统实现不能对任何特定探针包作死连接对待
- 规范建议keepalive保活包不应该包含数据,但也可以包含1个无意义的字节,比如0x0。
- SEG.SEQ = SND.NXT-1,即TCP保活探测报文序列号将前一个TCP报文序列号减1。SND.NXT = RCV.NXT,即下一次发送正常报文序号等于ACK序列号;总之保活报文不在窗口控制范围内 有一张图,可以很容易说明,但请仔细观察Tcp Keepalive部分:
Tcp keepalive 如何使用
以下环境是在Linux服务器上进行。应用程序若想使用,需要设置SO_KEEPALIVE套接口选项才能够生效。系统内核参数配置
tcp_keepalive_time
,在TCP保活打开的情况下,最后一次数据交换到TCP发送第一个保活探测包的间隔,即允许的持续空闲时长,或者说每次正常发送心跳的周期,默认值为7200s(2h)。tcp_keepalive_probes
在tcp_keepalive_time之后,没有接收到对方确认,继续发送保活探测包次数,默认值为9(次)tcp_keepalive_intvl
,在tcp_keepalive_time之后,没有接收到对方确认,继续发送保活探测包的发送频率,默认值为75s。
发送频率tcp_keepalive_intvl
乘以发送次数tcp_keepalive_probes
,就得到了从开始探测到放弃探测确定连接断开的时间;
若设置,服务器在客户端连接空闲的时候,每90秒发送一次保活探测包到客户端,若没有及时收到客户端的TCP Keepalive ACK确认,将继续等待15秒*2=30秒。总之可以在90s+30s=120秒(两分钟)时间内可检测到连接失效与否。
以下改动,需要写入到/etc/sysctl.conf
文件:
保存退出,然后执行net.ipv4.tcp_keepalive_time=90
net.ipv4.tcp_keepalive_intvl=15
net.ipv4.tcp_keepalive_probes=2
sysctl -p
生效。可通过sysctl -a | grep keepalive
命令检测一下是否已经生效。
针对已经设置SO_KEEPALIVE
的套接字,应用程序不用重启,内核直接生效。Java/netty服务器如何使用
只需要在服务器端一方设置即可,客户端完全不用设置,比如基于netty 4服务器程序:
Java程序只能做到设置ServerBootstrap b = new ServerBootstrap();
b.group(bossGroup, workerGroup)
.channel(NioServerSocketChannel.class)
.option(ChannelOption.SO_BACKLOG, 100)
.childOption(ChannelOption.SO_KEEPALIVE, true)
.handler(new LoggingHandler(LogLevel.INFO))
.childHandler(new ChannelInitializer() { @Override
public void initChannel(SocketChannel ch) throws Exception {
ch.pipeline().addLast( new EchoServerHandler());
}
}); // Start the server.
ChannelFuture f = b.bind(port).sync(); // Wait until the server socket is closed.
f.channel().closeFuture().sync();
SO_KEEPALIVE
选项,至于TCP_KEEPCNT,TCP_KEEPIDLE,TCP_KEEPINTVL
等参数配置,只能依赖于sysctl
配置,系统进行读取。