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 的建议,一直在见缝捡漏。

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

相关推荐
米羊12120 小时前
fastjson (3修复)
网络·网络协议·安全
oMcLin1 天前
Ubuntu 22.04 无法连接外部网络的故障排查与解决(解决 DNS 配置问题)
linux·网络·ubuntu
小李独爱秋1 天前
计算机网络经典问题透视:常规密钥体制与公钥体制最主要的区别是什么?—— 一文带你从“钥匙”看懂现代密码学核心
服务器·网络·tcp/ip·计算机网络·密码学
千百元1 天前
限制网段访问服务器端口63790
java·网络·mybatis
bst@微胖子1 天前
Linux下排查网络偶现超时问题
linux·网络·dubbo
几道之旅1 天前
websocket.WebSocketApp是全双工的吗?
网络·websocket·网络协议
OpenMiniServer1 天前
JsonKV协议技术文档
linux·服务器·网络
2501_924064111 天前
2025年APP隐私合规测试主流方法与工具深度对比
大数据·网络·人工智能
开开心心就好1 天前
OCR识别工具可加AI接口,快捷键截图翻译便捷
java·网络·windows·随机森林·电脑·excel·推荐算法
bst@微胖子1 天前
CrewAI+FastAPI实现健康档案智能体项目
网络·fastapi