如何在Python中重置TCP套接字?

时间:2017-09-17 13:05:45

标签: python linux sockets tcp

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

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

我发现在Linux中可以通过使用系列AF_UNSPEC的地址调用connect来reset a TCP connnection。但我还没有找到一种方法来从Python程序中做到这一点。

我如何connect使用Python中的AF_UNSPEC地址?

到目前为止我尝试了什么

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

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可能不是我问题的答案。

2 个答案:

答案 0 :(得分:0)

看看CPython中当前的socket软件包implementation,实际上没有pythonic的方法(从2019年1月起,将套接字连接到AF_UNSPEC地址(即{ {3}}。

下一个最好的事情是在接受的套接字上设置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

答案 1 :(得分:-1)

您可以尝试使用SO_LINGER套接字选项(setsockopt),并将延迟时间设置为0. close,套接字SO_LINGER设置为0秒延迟时间将导致RST而不是FIN。