tcp shutdown, fin_wait1, fin_wait2, close_wait, last_ack, 谢特!

TCP 作为双向传输协议,如果你想只收不发,可以单向关掉发,shutdown(socket.SHUT_WR),但不建议这么做。

看以下代码:

python 复制代码
#!/Users/zhaoya/myenv/bin/python3
# client
import socket

client_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
server_address = ('127.0.0.1', 12345)
client_socket.connect(server_address)
client_socket.shutdown(socket.SHUT_WR) # 单向关闭发

while True:
    try:
        data = client_socket.recv(1024)
        if not data:
            break
        print(f"recv: {data.decode()}")
    except:
        break

client_socket.close()
python 复制代码
#!/Users/zhaoya/myenv/bin/python3
# server
import socket

server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
server_address = ('127.0.0.1', 12345)
server_socket.bind(server_address)
server_socket.listen(1)

client_socket, client_address = server_socket.accept()

while True:
    try:
        data = "xxxxx"
        client_socket.send(data.encode())
        print(f"send: {data}")
    except:
        break

client_socket.close()
server_socket.close()

运行后:

状态很奇怪,人们很难接受一个连接在非 ESTABLISHED 状态下正常传输数据,这会引来一堆咨询,但根据 TCP 状态机,这又是合理的,client 单向关闭了发送,FIN 就过去了,server 回复了 FIN,进入 CLOSE_WAIT,client 收到回复进入 FIN_WAIT_2,而 server 在 CLOSE_WAIT 下发数据,client 在 FIN_WAIT_2 下是完全合理的。

为了少惹是生非,换一个方向,在 server 侧关闭掉收,client_socket.shutdown(socket.SHUT_RD),就香了:

pyhon 复制代码
client_socket, client_address = server_socket.accept()
client_socket.shutdown(socket.SHUT_RD) # 单向关闭收

此时状态很正常:

故,shutdown 的 SHUT_RD 仅影响 socket 接口(仅仅在读的时候报错,让你读不出去),不影响 TCP 状态。

再看纷扰的 FIN_WAIT_1/2。

TCP 的 RFC793 并未规定状态超时时间,为完整闭环这是合理的。但至少 Linux kernel 的 TCP 实现用不同的机制指定了 FIN_WAIT_1/2 的超时时间:

  • FIN_WAIT_1 超时时间由 net.ipv4.tcp_orphan_retries 控制:如果一直收不到针对 FIN 的 ACK,在彻底销毁这个 FIN_WAIT_1 连接前,等待的 RTO 退避次数;
  • FIN_WAIT_2 超时时间由 net.ipv4.tcp_fin_timeout 控制:默认值通常 60 秒。

这就带来了问题,如果按照上述第一种在 client 关闭发送方向的做法,连接进入 FIN_WAIT_2 状态,在 tcp_fin_timeout 之后连接将不可用(进入 TIME_WAIT),这可能并不符合 shutdown(WR) 调用者的意图。

换句话说 CLOSE_WAIT 明确在说 "我已经收到了对端的 FIN,正等着应用程序调用 shutdown + close 结束连接",这意思是,只要一端发送了 FIN,另一端应用程序在一个预期的不太久的时间内关闭连接是意料之中的,也就是不建议连接在 CLOSE_WAIT + FIN_WAIT_2 的状态继续传输数据。但如果应用程序希望如此呢?只能在对端 shutdown(RD) 了。

TCP 挥手断连期间的其它超时时间相对还好理解,比如 LAST_ACK 状态的超时时间和 FIN_WAIT_1 一致,由 net.ipv4.tcp_orphan_retries 控制。

So?TCP 的挥手状态太复杂且歧义了,一般的咨询类问题也都围绕着这些状态,加上 "应用程序忘了 close","没有 shutdown 优雅关闭","多线程 socket 描述继承 + 优雅关闭" 等等问题更让人抓狂。

更合理的做法应将协议实现部分单独抽出一个控制面,用 keepalive 来 probe 连接,而不是靠内置状态的超时机制。TCP 标准规定某些状态可能就是无限等待,实现就不能自行安排超时。然而由于 TCP 一开始即带内控制,keepalive 可能是另一个问题(采用 out of window 序列号)而不是方案,参见 对 oow 的建议,一直在见缝捡漏。

浙江温州皮鞋湿,下雨进水不会胖。

相关推荐
小林熬夜学编程18 分钟前
【Linux网络编程】第十四弹---构建功能丰富的HTTP服务器:从状态码处理到服务函数扩展
linux·运维·服务器·c语言·网络·c++·http
Hacker_Fuchen19 分钟前
天融信网络架构安全实践
网络·安全·架构
上海运维Q先生22 分钟前
面试题整理15----K8s常见的网络插件有哪些
运维·网络·kubernetes
ProtonBase32 分钟前
如何从 0 到 1 ,打造全新一代分布式数据架构
java·网络·数据库·数据仓库·分布式·云原生·架构
fantasy_arch10 小时前
CPU性能优化-磁盘空间和解析时间
网络·性能优化
njnu@liyong11 小时前
图解HTTP-HTTP报文
网络协议·计算机网络·http
是Dream呀12 小时前
Python从0到100(七十八):神经网络--从0开始搭建全连接网络和CNN网络
网络·python·神经网络
kaixin_learn_qt_ing13 小时前
了解RPC
网络·网络协议·rpc
安全小王子13 小时前
Kali操作系统简单介绍
网络·web安全
Hacker_LaoYi15 小时前
【漏洞分析】DDOS攻防分析(四)——TCP篇
网络·tcp/ip·ddos