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

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

相关推荐
幽兰的天空5 小时前
介绍 HTTP 请求如何实现跨域
网络·网络协议·http
lisenustc5 小时前
HTTP post请求工具类
网络·网络协议·http
心平气和️5 小时前
HTTP 配置与应用(不同网段)
网络·网络协议·计算机网络·http
心平气和️5 小时前
HTTP 配置与应用(局域网)
网络·计算机网络·http·智能路由器
Gworg6 小时前
网站HTTP改成HTTPS
网络协议·http·https
Mbblovey6 小时前
Picsart美易照片编辑器和视频编辑器
网络·windows·软件构建·需求分析·软件需求
北顾南栀倾寒7 小时前
[Qt]系统相关-网络编程-TCP、UDP、HTTP协议
开发语言·网络·c++·qt·tcp/ip·http·udp
GZ_TOGOGO7 小时前
PIM原理与配置
网络·华为·智能路由器
7ACE7 小时前
Wireshark TS | 虚假的 TCP Spurious Retransmission
网络·网络协议·tcp/ip·wireshark·tcpdump
大丈夫立于天地间8 小时前
ISIS基础知识
网络·网络协议·学习·智能路由器·信息与通信