TCP 协议中有长连接和短连接之分。短连接环境下,数据交互完毕后,主动释放连接。
长连接的环境下,进行一次数据交互后,很长一段时间内无数据交互时,客户端可能意外断电、死机、崩溃、重启,还是中间路由网络无故断开,这些 TCP 连接并未来得及正常释放,那么,连接的另一方并不知道对端的情况,它会一直维护这个连接,长时间的积累会导致非常多的半打开连接,造成端系统资源的消耗和浪费,且有可能导致在一个无效的数据链路层面发送业务数据,结果就是发送失败。所以服务器端要做到快速感知失败,减少无效链接操作,这就有了 TCP 的 Keepalive(保活探测)机制。
当一个 TCP 连接建立之后,启用 TCP Keepalive 的一端便会启动一个计时器,当这个计时器数值到达 0 之后(也就是经过 tcp_keep-alive_time 时间后,这个参数之后会讲到),一个 TCP 探测包便会被发出。这个 TCP 探测包是一个纯 ACK 包(规范建议,不应该包含任何数据,但也可以包含 1 个无意义的字节,比如 0x0。),其 Seq 号与上一个包是重复的,所以其实探测保活报文不在窗口控制范围内。
如果一个给定的连接在两小时内(默认时长)没有任何的动作,则服务器就向客户发一个探测报文段,客户主机必须处于以下 4 个状态之一:
对于 linux 内核来说,应用程序若想使用 TCP Keepalive,需要设置 SO_KEEPALIVE 套接字选项才能生效。有三个重要的参数:
其他编程语言有相应的设置方法,这里只谈 linux 内核参数的配置。例如 C 语言 中的 setsockopt() 函数,java 的 Netty 服务器框架中也提供了相关接口。
探测连接的对端是否存活
在应用交互的过程中,可能存在以下几种情况:
利用保活探测功能,可以探知这种对端的意外情况,从而保证在意外发生时,可以释放半打开的 TCP 连接。
防止中间设备因超时删除连接相关的连接表
中间设备如防火墙等,会为经过它的数据报文建立相关的连接信息表,并为其设置一个超时时间的定时器,如果超出预定时间,某连接无任何报文交互的话,
中间设备会将该连接信息从表中删除,在删除后,再有应用报文过来时,中间设备将丢弃该报文,从而导致应用出现异常。
Keepalive 技术只是 TCP 技术中的一个可选项。因为不当的配置可能会引起一些问题,所以默认是关闭的。可能导致下列问题:
很多人会把 TCP Keepalive 和 HTTP Keep-Alive 这两个概念搞混淆。这里简单介绍下 HTTP Keep-Alive。
在 HTTP/1.0 中,默认使用的是短连接。也就是说,浏览器和服务器每进行一次 HTTP 操作,就建立一次连接,但任务结束就中断连接。如果客户端浏览器访问的某个 HTML 或其他类型的 Web 页中包含有其他的 Web 资源,如 JavaScript 文件、图像文件、CSS 文件等;当浏览器每遇到这样一个 Web 资源,就会建立一个 HTTP 会话。
但从 HTTP/1.1起,默认使用长连接,用以保持连接特性。使用长连接的 HTTP 协议,会在响应头加上 Connection、Keep-Alive 字段。
HTTP 协议的 Keep-Alive 意图在于 TCP 连接复用,同一个连接上串行方式传递请求-响应数据;TCP 的 Keepalive 机制意图在于探测连接的对端是否存活。