异步客户端:未调用“connection\u lost”,导致ResourceWarning

2024-05-13 12:32:14 发布

您现在位置:Python中文网/ 问答频道 /正文

我正在使用asyncio客户端连接,然后断开与服务器的连接。在

  • 如果我连接到同一台计算机上的服务器程序,连接会很好地关闭。添加:当我开始向连接写入数据时,这个连接有时也会发出警告。请参阅下面的第二个代码版本。

  • 如果我连接到本地网络上的设备,我将得到ResourceWarning的未关闭传输。

如何正确关闭连接?

我在Windows7(64位)上使用Python3.6.0(32位)。在

第一次尝试

相关代码:

import asyncio
import logging
import warnings

class ClientConnection(asyncio.Protocol):
    def connection_made(self, transport):
        self.transport = transport
        transport.write_eof()  # Send EOF to close connection

    def connection_lost(self, exception):
        self.transport.close()
        super().connection_lost(exception)

def main():
    logging.basicConfig(level=logging.DEBUG)
    eventLoop = asyncio.get_event_loop()
    eventLoop.set_debug(True)
    warnings.simplefilter('always', ResourceWarning)  # enables ResourceWarning
    #co = eventLoop.create_connection(ClientConnection, '127.0.0.1', 7001)     # Works without warning
    co = eventLoop.create_connection(ClientConnection, '192.168.10.66', 7001)  # Gives warning
    try:
        eventLoop.run_until_complete(co)
    finally:
        eventLoop.close()

if __name__ == "__main__":
    main()

控制台输出:

^{pr2}$

第二次尝试

我对代码做了以下更改:

  • connection_lost中删除{}
  • 将一些数据写入连接
  • 添加了data_received和{}回调
  • 添加了更多调试日志

观察结果:

  • 我试图将transport.close()添加到connection_made中,但它总是会得到OSError: [WinError 10038]注意:这可能是另一个问题,所以让我们暂时忽略它,假设我不会这样做。
  • 当向套接字写入一些数据时,localhost连接也开始发出警告,但并不总是这样。在
  • 当它发出警告时,connection_lost不被调用。为什么?在

修改代码:

import asyncio
import logging
import warnings

class ClientConnection(asyncio.Protocol):
    def connection_made(self, transport):
        logging.debug('connection_made')
        self.transport = transport
        transport.write(b'1234\r')
        transport.write_eof()  # Send EOF to close connection
        #transport.close()  # Cannot close here either, gives 'OSError: [WinError 10038]'

    def connection_lost(self, exception):
        logging.debug('connection_lost')
        super().connection_lost(exception)

    def data_received(self, data):
        logging.debug('received {} bytes'.format(len(data)))

    def eof_received(self):
        logging.debug('EOF received')
        self.transport.close()

def main():
    logging.basicConfig(level=logging.DEBUG)
    eventLoop = asyncio.get_event_loop()
    eventLoop.set_debug(True)
    warnings.simplefilter('always', ResourceWarning)  # enables ResourceWarning
    #co = eventLoop.create_connection(ClientConnection, '127.0.0.1', 7001)     # Works without warning
    co = eventLoop.create_connection(ClientConnection, '192.168.10.66', 7001)  # Gives warning
    try:
        eventLoop.run_until_complete(co)
        logging.debug('done')
    finally:
        eventLoop.close()

if __name__ == "__main__":
    main()

成功时输出:

...
DEBUG:root:EOF received
DEBUG:root:connection_lost
DEBUG:root:done
DEBUG:asyncio:Close <_WindowsSelectorEventLoop running=False closed=False debug=True>

失败时的输出(注意缺少connection_lost):

...
DEBUG:root:EOF received
DEBUG:root:done
DEBUG:asyncio:Close <_WindowsSelectorEventLoop running=False closed=False debug=True>
sys:1: ResourceWarning: unclosed <socket.socket fd=240, family=AddressFamily.AF_INET, type=SocketKind.SOCK_STREAM, proto
=6, laddr=('127.0.0.1', 63858), raddr=('127.0.0.1', 7001)>
C:\Program Files (x86)\Python36-32\lib\asyncio\selector_events.py:631: ResourceWarning: unclosed transport <_SelectorSoc
ketTransport closing fd=240>
  source=self)

Tags: debugimportselfasynciocloseloggingdefeventloop
2条回答

请等待服务器关闭连接,或使用transport.close()关闭传输。这也将触发connection_lost(不要从connection_lost调用transport.close()!)公司名称:

class ClientConnection(asyncio.Protocol):
    def connection_made(self, transport):
        logging.debug("connection made, calling write eof")
        transport.write_eof()
        logging.debug("after calling write eof, calling close")
        transport.close()
        logging.debug("after calling close")

    def connection_lost(self, exception):
        logging.debug("connection lost")
        super().connection_lost(exception)

如果您再尝试一点,您可能还会为您的本地计算机获得一些ResourceWarning。例如,尝试在write_eof()之前添加transport.write(b'hello world!'),或者使本地服务器响应较慢。在

尝试运行循环多一点。下面是一个使用loop.run_forever()loop.stop()的示例,假设echo server在本地和远程计算机上:

import asyncio
import logging

import warnings


class ClientConnection(asyncio.Protocol):
    def connection_made(self, transport):
        logging.debug("connection made, sending and calling write eof")
        transport.write(b'hello')
        transport.write_eof()
        logging.debug("after calling write eof")

    def data_received(self, data):
        logging.debug("Got: {}".format(data))
        super().data_received(data)

    def connection_lost(self, exception):
        logging.debug("connection lost")
        super().connection_lost(exception)
        loop.stop()


def test_echo(ip, port):
    logging.debug("Creating connection: {}:{}".format(ip, port))
    co = loop.create_connection(ClientConnection, ip, port)
    logging.debug("Starting loop...")
    print(loop.run_until_complete(co))
    logging.debug("...create_connection done, running loop forever...")
    loop.run_forever()
    logging.debug("Loop stopped")

    logging.debug('        ')


if __name__ == "__main__":
    logging.basicConfig(level=logging.DEBUG)
    warnings.simplefilter('always')  # enables ResourceWarning

    loop = asyncio.get_event_loop()
    loop.set_debug(True)

    test_echo('127.0.0.1', 7001)
    test_echo('54.175.103.105', 30000)

    logging.debug("done")
    loop.close()

相关问题 更多 >