http://blog.csdn.net/zhangskd/article/details/44177475
TCP的Keepalive,目的在于看看对方有没有发生异常,如果有异常就及时关闭连接。
当传输双方不主动关闭连接时,就算双方没有交换任何数据,连接也是一直有效的。
如果这个时候对端、中间网络出现异常而导致连接不可用,本端如何得知这一信息呢?
答案就是保活定时器。它每隔一段时间会超时,超时后会检查连接是否空闲太久了,如果空闲的时间超过
了设置时间,就会发送探测报文。然后通过对端是否响应、响应是否符合预期,来判断对端是否正常,
如果不正常,就主动关闭连接。
当服务器发送探测报文时,客户端可能处于4种不同的情况:仍然正常运行、已经崩溃、已经崩溃并重启了、
由于中间链路问题不可达。在不同的情况下,服务器会得到不一样的反馈。
(1) 客户主机依然正常运行,并且从服务器端可达
客户端的TCP响应正常,从而服务器端知道对方是正常的。保活定时器会在两小时以后继续触发。
(2) 客户主机已经崩溃,并且关闭或者正在重新启动
客户端的TCP没有响应,服务器没有收到对探测包的响应,此后每隔75s发送探测报文,一共发送9次。
socket函数会返回-1,errno设置为ETIMEDOUT,表示连接超时。
(3) 客户主机已经崩溃,并且重新启动了
客户端的TCP发送RST,服务器端收到后关闭此连接。
socket函数会返回-1,errno设置为ECONNRESET,表示连接被对端复位了。
(4) 客户主机依然正常运行,但是从服务器不可达
双方的反应和第二种是一样的,因为服务器不能区分对端异常与中间链路异常。
socket函数会返回-1,errno设置为EHOSTUNREACH,表示对端不可达。
内核默认并不使用TCP Keepalive功能,除非用户设置了SO_KEEPALIVE选项。
有两种方式可以自行调整保活定时器的参数:一种是修改TCP参数,一种是使用TCP层选项。
(1) TCP参数
tcp_keepalive_time
最后一次数据交换到TCP发送第一个保活探测报文的时间,即允许连接空闲的时间,默认为7200s。
tcp_keepalive_intvl
保活探测报文的重传时间,默认为75s。
tcp_keepalive_probes
保活探测报文的发送次数,默认为9次。
Q:一次完整的保活探测需要花费多长时间?
A:tcp_keepalive_time + tcp_keepalive_intvl * tcp_keepalive_probes,默认值为7875s。
如果觉得两个多小时太长了,可以自行调整上述参数。
(2) TCP层选项
TCP_KEEPIDLE:含义同tcp_keepalive_time。
TCP_KEEPINTVL:含义同tcp_keepalive_intvl。
TCP_KEEPCNT:含义同tcp_keepalive_probes。
Q:既然有了TCP参数可供调整,为什么还增加了上述的TCP层选项?
A:TCP参数是面向本机的所有TCP连接,一旦调整了,对所有的连接都有效。
而TCP层选项是面向一条连接的,一旦调整了,只对本条连接有效。
激活
在连接建立后,可以通过设置SO_KEEPALIVE选项,来激活保活定时器。
int keepalive = 1;
setsockopt(fd, SOL_SOCKET, SO_KEEPALIVE, &keepalive, sizeof(keepalive));
可以使用TCP层选项来动态调整保活定时器的参数。
int keepidle = 600;
int keepintvl = 10;
int keepcnt = 6;
setsockopt(fd, SOL_TCP, TCP_KEEPIDLE, &keepidle, sizeof(keepidle));
setsockopt(fd, SOL_TCP, TCP_KEEPINTVL, &keepintvl, sizeof(keepintvl));
setsockopt(fd, SOL_TCP, TCP_KEEPCNT, &keepcnt, sizeof(keepcnt));