首页 > 解决方案 > Python 3.8 TLSv1.3 套接字关闭导致 ConnectionResetError 或 ConnectionAbortedError

问题描述

客户端:

data = b'\xff' * 1000000
ssock = socket.socket(socket.AF_INET, socket.SOCK_STREAM, 0)
#context is created by ssl.create_default_context(ssl.Purpose.SERVER_AUTH)
ssock = context.wrap_socket(ssock, server_hostname='xd1337sv')
ssock.connect((SERVERADDR, SERVERPORT))
ssock.sendall(data)
#time.sleep(3)
ssock.close()

如果我只使用常规的非 SSL 套接字,那么在服务器接收准确数据量的情况下一切正常。如果我使用 TLS 套接字,则行为取决于版本。

如果我在 Python 3.6 上运行服务器或客户端,因此将使用 TLSv1.2,则没有问题。只有在使用 TLSv1.3 时才会出现问题,并且取决于data客户端ssocket.close()行的大小和执行速度。

如果我根据数据的大小放入适量的time.sleep之前ssocket.close(),那么我不会出错。否则,服务器将ConnectionResetError [WinError 10054] An existing connection was forcibly closed by the remote host只获取和接收部分数据,或者抛出ConnectionAbortedError [WinError 10053] An established connection was aborted by the software in your host machine和接收没有数据。

我正在使用本地地址在本地计算机上测试服务器和客户端192.168.1.2

标签: pythonpython-3.xsocketsssl

解决方案


不同之处在于 TLS 1.3 在 TLS 握手之后发送会话票证,而在以前的 TLS 版本中,会话票证是在 TLS 握手内发送的。因此,使用 TLS 1.3 来自服务器的数据(会话票证)将在ssock.connect(...)完成后到达。由于您的应用程序在关闭套接字后不会读取任何数据,而未读取的connect数据仍在底层 TCP 套接字的套接字缓冲区内。这将导致 RST 发送到服务器并导致连接重置错误。

这是从不尝试从服务器读取的应用程序的一个已知问题。如果应用程序期望来自服务器的响应并使用recv它来获取它,这将隐式地读取会话票证。

当您不希望服务器返回任何应用程序数据时,要解决这种情况,请在关闭套接字之前正确关闭套接字。由于这将读取服务器 SSL 关闭消息,它还将隐式读取服务器之前发送的会话票证。

try:
    ssock = ssock.unwrap()
except:
    True
ssock.close()

有关详细信息,另请参阅此问题此文档


推荐阅读