【问题标题】:How can I reset a TCP socket in Python?如何在 Python 中重置 TCP 套接字?
【发布时间】:2017-09-17 13:05:44
【问题描述】:

我有一个用 Python 编写的套接字代理,当它从一对通信对等方接收到 RST 时,将通过让套接字被垃圾收集来关闭与两个对等方的连接。这会导致其他对等方看到 FIN 而不是 RST。

这意味着代理有效地将 RST 转换为 FIN,我认为这并不理想。

我发现在 Linux 中,可以通过使用家庭地址 AF_UNSPEC 调用 connect 来连接到 reset a TCP connnection。但是我还没有从 Python 程序中找到一种方法。

如何在 Python 中将connect 发送到AF_UNSPEC 地址?

到目前为止我所尝试的

我尝试查看相关connect 方法的help 输出并发现:

Help on built-in function connect:

connect(...)
    connect(address)

    Connect the socket to a remote address.  For IP sockets, the address
    is a pair (host, port).

不幸的是,这并没有告诉我 address 参数必须是什么才能构造 AF_UNSPEC 地址。

我试图将原始套接字 fd 包装在一个带有家族 AF_UNSPEC 的新套接字对象中,如下所示:

socket.fromfd(s.fileno(), socket.AF_UNSPEC, 0)

生成的对象会产生相同的帮助文本,任何尝试在新构造的套接字对象上调用 connect 都会导致

socket.error: getsockaddrarg: bad family

所以看起来使用socket.fromfd 可能不是我问题的答案。

【问题讨论】:

    标签: python linux sockets tcp


    【解决方案1】:

    查看 CPython 中当前的socketimplementation,确实没有pythonic 方式(将套接字连接到AF_UNSPEC 地址,截至2019-01(即到reset the connection on Linux)。

    下一个最好的方法是在接受的套接字上设置SO_LINGER 选项(直接或通过继承)。当启用延迟(并设置为零超时)时,关闭套接字会重置连接。

    您必须小心在正确的套接字 API 级别设置 SO_LINGER 选项,并为选项值使用正确的编码(它是一个结构)。

    例子:

    import socket
    import struct
    import time
    
    s = socket.socket(socket.AF_INET6)
    s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
    s.setsockopt(socket.IPPROTO_IPV6, socket.IPV6_V6ONLY, 0)
    # if we want to inherit this option:
    #s.setsockopt(socket.SOL_SOCKET, socket.SO_LINGER, struct.pack('ii', 1, 0))
    s.bind(('', 2323))
    s.listen()
    con, addr = s.accept()
    con.setsockopt(socket.SOL_SOCKET, socket.SO_LINGER, struct.pack('ii', 1, 0))
    time.sleep(1)
    con.close()
    time.sleep(3)
    

    用 curl 连接到这个端口:

    $ curl localhost:2323
    curl: (56) Recv failure: Connection reset by peer
    

    连接到这个端口而不发送任何东西:

    $ socat - tcp:localhost:2323
    

    当用例如转储数据包时

    $ tshark -i lo -f 'tcp port 2323'
    

    最后一个数据包应该是 RST(从服务器发送到客户端),在这两种情况下 - 例如:

    39 9758.478140247    127.0.0.1 → 127.0.0.1    TCP 66 2323 → 34494 [RST, ACK]
            Seq=1 Ack=1 Win=43776 Len=0 TSval=2787120418 TSecr=2787119417
    

    【讨论】:

    • 我必须先修复您的示例中的几个错误,然后它才能运行 TypeError: listen() takes exactly one argument (0 given)NameError: name 'struct' is not defined。一旦我修复了这些,我看到的结果是,如果套接字上有尚未读取的数据,则会产生 RST,否则会产生 FIN。如果我不使用SO_LINGER,这与我看到的完全相同的行为。所以看起来你得到 RST 的原因是curl 发送了一个服务器没有读取的请求。但是当我想生成 RST 时,我不能强迫对方向我发送更多数据。
    • @kasperd,我已经更新了我的答案。我添加了缺少的 struct 导入并修复了 SO_LINGER 选项值。在启用延迟和零超时的情况下,我在这两种情况下都会得到 RST。请注意,自 Python 3.5 起,listen() 的参数是可选的。
    【解决方案2】:

    您可以尝试使用SO_LINGER 套接字选项(setsockopt),并将逗留时间设置为 0。closeSO_LINGER 设置为 0 秒逗留时间的套接字上将导致 RST 而不是 FIN。

    【讨论】:

    • 我测试了c.setsockopt(socket.SOL_IP, socket.SO_LINGER, 0),但没有成功。连接仍然以FIN 而不是RST 关闭,就像我想要的那样。
    猜你喜欢
    • 2011-09-20
    • 2012-01-15
    • 1970-01-01
    • 2018-07-09
    • 1970-01-01
    • 2012-01-03
    • 1970-01-01
    • 2015-08-04
    • 1970-01-01
    相关资源
    最近更新 更多