TCP 故障模型
TCP 故障模型
网络中断
- 端点(应用程序所在 LAN 或主机)之外发生的网络故障是临时的,因为路由协议会发现并绕开故障节点
- 端点出问题时,通常没有备用的路径,问题会一直存在直到故障修复
网络中断时的 TCP
- 除非中间路由器发送 ICMP 报文,指出目的网络或主机不可达,否则应用程序和 TCP/IP 协议栈都无法立即感知到网络中断的发生
- 收到 ICMP 后会返回
ENETUNREACH
或EHOSTUNREACH
错误
- 收到 ICMP 后会返回
- 发送端会不断超时重传没有被确认的段,源自 Berkeley 的实现中重传 12 次(大约九分钟)后就会放弃
- 读操作会返回
ETIMEDOUT
- 写操作会收到
SIGPIPE
信号或返回EPIPE
错误
- 读操作会返回
对端应用崩溃
对于应用程序来说,是无法区分对端应用崩溃和对端调用 close()
或 exit()
的
- 两种情况下,对端都会发送 FIN
- 这只是告诉我们,对端已经没有数据要发送了,并不代表对方不能再接收我们发出的数据
- select 会指示该套接字可读,read 后会返回 0(EOF)
对端应用崩溃的情况下,如果再发消息给对端,对端会回复 RST
- 再去读该套接字就会返回
ECONNRESET
- 再去写该套接字会收到
SIGPIPE
信号或返回EPIPE
错误 - 也就是说只有你向崩溃的对端写了一次后,下次读写才能收到通知
对端主机崩溃
- 对端主机重启前,该错误的表现和网络中断是相同的
- 如果超时前,对端主机重启了,重传的报文到达对端主机后,对端主机会回复 RST
- 再去读该套接字就会返回
ECONNRESET
- 再去写该套接字会收到
SIGPIPE
信号或返回EPIPE
错误
- 再去读该套接字就会返回
如果想要不主动发送消息,也检测到对端主机崩溃的情况,可以使用 TCP 的心跳机制
TCP 的心跳机制
给 TCP 套接字设置保活选项( SO_KEEPALIVE
)后,如果超过一定时间(默认为两小时)套接字的任一方向上都没有数据交换,TCP 就会给对端发送一个保活探测分节,对端必须响应该分节
可能发生的情况有
- 对端响应 ACK
- 应用程序不会收到通知
- 对端响应 RST
- 说明对端已崩溃且重新启动,待处理错误被设为
ECONNRESET
,套接字本身被关闭
- 说明对端已崩溃且重新启动,待处理错误被设为
- 无响应
- 源自 Berkeley 的实现会每隔一段时间再继续发送数个保活分节,如果这些分节都没有收到响应,待处理错误被设为
ETIMEOUT
,套接字本身被关闭
- 源自 Berkeley 的实现会每隔一段时间再继续发送数个保活分节,如果这些分节都没有收到响应,待处理错误被设为
- 如果套接字收到 ICMP 错误(比如目的地不可达) 作为响应,那么返回对应的错误,套接字本身被关闭
该选项一般用于检测对方主机是否崩溃或不可达,因为对端进程崩溃的情况下,TCP 会发送 FIN,可以通过 select 检测到(select 会指示套接字可读)
🔑 即使保活分节没有相应,也不能确定对端主机崩溃了,因为中间路由器可能崩溃,所以保活可能会切断一个正常的连接
一般由服务器设置该选项,用来检测并终止半开连接
- 因为服务器往往不会主动发送消息给客户
- 那么就会出现客户机崩溃后,服务器还一直在等待客户的输入,这种连接被称为半开连接
Linux 配置参数
Linux 下可以通过配置以下参数改变 keepalive 的行为
tcp_keepalive_time
- 超时时间,即多久没有数据交换会发送心跳包
tcp_keepalive_intvl
- 没收到响应的情况下,隔多久重新发送
tcp_keepalive_probes
- 没收到响应的情况下,会重新发送多少次
可以通过 proc文件系统和 sysctl 调整这些参数
cat /proc/sys/net/ipv4/tcp_keepalive_time // 查询配置
echo 600 > /proc/sys/net/ipv4/tcp_keepalive_time // 更改配置
🔑 如果想要更细粒度的保活机制,应该在应用层自己实现,而不是去改动这些配置,因为这些配置一经改动影响的是系统上的所有 TCP 连接
Linux 中还可以通过这些选项对每条连接设置不同的配置,追求兼容性的程序不应该使用这些选项
TCP_KEEPIDLE
TCP_KEEPINTVL
TCP_KEEPCNT
参考资料:UNIX 网络编程
本文来自博客园,作者:路过的摸鱼侠,转载请注明原文链接:https://www.cnblogs.com/ljx-null/p/16470364.html