Socket的shutdown()与close()差异:替换后curl报错原因解析
Socket中close()引发curl连接重置的原因
场景复现
正常运行的服务端代码
if __name__ == "__main__": soc = socket.socket(socket.AF_INET, socket.SOCK_STREAM) soc.bind(("127.0.0.1", 4000)) while True: soc.listen(5) conn, address = soc.accept() data = conn.recv(32) print(f"got data: {data}") conn.sendall(response.encode()) conn.shutdown(socket.SHUT_WR)
修改后触发错误的代码
将代码中的conn.shutdown(socket.SHUT_WR)替换为conn.close():
if __name__ == "__main__": soc = socket.socket(socket.AF_INET, socket.SOCK_STREAM) soc.bind(("127.0.0.1", 4000)) while True: soc.listen(5) conn, address = soc.accept() data = conn.recv(32) print(f"got data: {data}") conn.sendall(response.encode()) conn.close()
测试命令及错误响应
执行curl命令:
curl http://localhost:4000
收到错误响应:
curl: (56) Recv failure: Connection was reset
中文翻译:curl: (56) 接收失败:连接已重置
另外存在两个特殊现象:
- 使用
shutdown(socket.SHUT_WR)时运行完全正常 - 在
close()后添加time.sleep()或打印操作,也能正常运行
原因分析
核心差异在于shutdown和close对TCP连接的处理逻辑:
shutdown(SHUT_WR)的行为:
这是TCP半关闭操作,仅关闭连接的写方向,告知客户端“服务端已无数据发送”,但读方向仍保持开放。此时服务端会向curl发送FIN包,curl收到后可正常完成响应读取流程,按TCP四次握手关闭连接,不会触发重置。close()的行为:
close()会直接关闭套接字的读写双向。问题出在调用时机:当sendall()执行完毕后,响应数据可能仍在TCP发送缓冲区中,未完全发送到网络。此时立刻调用close(),操作系统可能直接发送RST包终止连接,而非等待缓冲区数据发送完成并完成四次握手。curl在等待完整HTTP响应时收到RST包,就会抛出“连接已重置”错误。添加sleep/打印后正常的原因:
这类操作给了TCP缓冲区足够时间,将响应数据完全发送到网络,完成与curl的交互后再关闭套接字,自然不会触发RST。
注:每次curl都是新连接,但问题出在单个连接的关闭流程上,与新连接本身无关。
内容的提问来源于stack exchange,提问作者mohamed naser
相关产品推荐
相关产品推荐

