Python 3.8 TLSv1.3 套接字关闭会导致 ConnectionResetError 或 Connectio



客户端:

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()行的执行速度。

如果我根据数据的大小在ssocket.close()之前放置适量的time.sleep,那么我不会出错。否则,服务器将获得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在本地计算机上测试服务器和客户端。

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

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

若要在不希望服务器返回任何应用程序数据时解决此问题,请在关闭套接字之前对其进行适当的 SSL 关闭。由于这将读取服务器SSL关闭消息,因此它还将隐式读取服务器之前发送的会话票证。

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

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

当应用程序使用证书通过 gunicorn 运行时,我遇到了类似的问题。jsondecodeerror问题随机来到客户端,即响应为空。唯一使用TLS 1.2的东西。

解决方案很简单,我在uwsgi上部署了应用程序,问题消失了

最新更新