比如,一条普通的http连接底层依赖了一条普通的tcp连接,当某些故障导致tcp断掉了,http如何感知呢?
结论:tcp连接本质上是逻辑上的连接,依靠两边的操作系统内核维护了一些变量来表示连接的不同状态,所以只有在两端间真正有数据包传送的时候,才可能会改变现有的逻辑状态到一个新状态。
以下实验已将socket keepalive关闭
实验过程
client 与 server 建立TCP连接
通过scapy构造RST包“欺骗”server关闭连接来模拟链路故障,此时client并不知情server端已关闭(send(IP(dst=“192.168.57.3”)/TCP(sport=42752, dport=12345, seq=992635624, flags=“R”)))
client发送数据
可以看到server由内核代为返回了RST包
抓包详情如下:

其中称192.168.57.100为client,192.168.57.3为server
每个包解释如下:
seq=1-3,TCP三次握手建立连接
seq=12,用scapy构造的欺骗包,让server误以为client要断开连接,从而server释放掉该tcp连接
seq=13,client认为连接仍然正常,使用send()函数发送字符串"hello world"给server
seq=14,server端内核发现13号数据包对应的socket不存在,遂发送RST包提示对方该连接无效,也就是14号包
实验过程
client 与 server 建立TCP连接
通过scapy构造RST包“欺骗”server关闭连接来模拟链路故障,此时client并不知情server端已关闭
client 进入 recv开始阻塞接收
实验效果:client一直阻塞在recv处
抓包详情如下:

可以看到在发送seq=20的RST欺骗包后,随后双方无任何数据沟通。导致了server端TCP连接已关闭,但client端不知情,一直阻塞。
这里的“client端不知情”说的稍微明白些其实是“client 的内核不知情”,如果在“内核知情后”,再进入recv,会有什么效果呢?
开篇提到的http是比较符合这种情况的,因为http是由request(发送数据)和response(接受数据)构成的
实验过程
client 与 server 建立TCP连接
通过scapy构造RST包“欺骗”server关闭连接来模拟链路故障,此时client并不知情server端已关闭
client发送数据
client接收数据
实验效果:send发送返回成功,recv返回“Connection reset by peer”
抓包详情:

数据包情况跟实验1完全一致,client发送了15号数据包,server返回16号RST包通知到client内核,待到client执行recv时,内核直接返回“Connection reset by peer”的信息提示应用层。