You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Python3 Socket Server:如何确保conn.recv()的ACK在conn.close前发送?

如何确保Socket在调用close前发送ACK给客户端的FIN包?

我用WireShark抓包分析以下代码的TCP交互时发现,客户端关闭Socket后,服务器端的conn.recv()并没有发送ACK包。

抓包显示的交互流程为:

client >> server : FIN/ACK
server >> client : FIN/ACK

问题根源在于conn.close()在conn.recv()对应的ACK包发送之前就执行了。取消注释代码里的time.sleep(1)就能验证这一点——此时抓包流程变成:

client >> server : FIN/ACK
server >> client : ACK
server >> client : FIN/ACK

请问要怎么配置Socket,才能确保conn.recv()对应的ACK包在后续的conn.close()执行前就被发送出去?

相关代码

import socket  # 补充原代码缺失的导入

class PLC_Server:
    def __init__(self, ip_address="localhost", port=5000):
        self.ip = ip_address
        self.port = port
        self.comm = {"address": self.ip + ":" + str(self.port)}
        self.buffer_size = 4096
        self.client = None

    def _init_socket(self, listen_num=1):
        self.sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        self.sock.bind((self.ip, int(self.port)))
        self.sock.listen(listen_num)

    def run(self):
        self._init_socket()
        while True:
            conn, address = self.sock.accept()
            while True:
                recv_data = conn.recv(4096)

                if recv_data == b"":
                    # FIN被conn.recv转换为长度为0的消息
                    print("Detect Client's socket close")
                    # time.sleep(1)
                    conn.close()
                    break
                else:
                    conn.send("OK")  # 注意:这里需要传bytes类型,原代码会报错

if __name__ == '__main__':
    test = PLC_Server()
    test.run()

解决方案

方法1:设置TCP_QUICKACK选项

TCP协议默认有延迟ACK机制(通常200ms左右),用来合并ACK和后续的发送数据。我们可以通过开启TCP_QUICKACK选项,强制TCP栈立即发送ACK包,不需要等待延迟定时器。

修改后的代码片段:

if recv_data == b"":
    print("Detect Client's socket close")
    # 开启TCP_QUICKACK,强制立即发送ACK
    conn.setsockopt(socket.IPPROTO_TCP, socket.TCP_QUICKACK, 1)
    conn.close()
    break
else:
    conn.send(b"OK")  # 修正为bytes类型

方法2:先shutdown再close

调用conn.shutdown(socket.SHUT_WR)会先关闭Socket的写方向,触发FIN包发送,但在此之前TCP栈会确保所有待发送的ACK都已发出。之后再调用close()释放Socket资源。

修改后的代码片段:

if recv_data == b"":
    print("Detect Client's socket close")
    # 先关闭写方向,确保ACK已发送
    conn.shutdown(socket.SHUT_WR)
    # 可选:读取一次确认客户端的响应(如果需要)
    # conn.recv(4096)
    conn.close()
    break
else:
    conn.send(b"OK")  # 修正为bytes类型

方法3:发送空数据(不推荐)

虽然可以通过发送一个空的bytes对象conn.send(b"")来触发ACK,但这种方式不够优雅,还会产生额外的无效TCP段,因此不建议使用。

额外注意

原代码中的conn.send("OK")会抛出TypeError,因为send()方法要求传入bytes类型数据,必须改为conn.send(b"OK")。

内容的提问来源于stack exchange,提问作者orilyn

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.16 08:30:50