Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

async client: `connection_lost` not called, results in ResourceWarning

I am using asyncio client to connect and then disconnect from server.

  • If I connect to server program on the same computer, connection closes fine. Add: When I started writing data to connection, this connection also started giving warnings sometimes. See second code version below.

  • If I connect to device on the local network, I get ResourceWarning for unclosed transport.

How do I close the connection correctly?

I am using Python 3.6.0 (32-bit) on Windows 7 (64-bit).

First attempt

Relevant code:

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()

Console output:

DEBUG:asyncio:Using selector: SelectSelector
DEBUG:asyncio:connect <socket.socket fd=240, family=AddressFamily.AF_INET, type=SocketKind.SOCK_STREAM, proto=6> to ('19
2.168.10.66', 7001)
DEBUG:asyncio:poll took 0.000 ms: 1 events
DEBUG:asyncio:<socket.socket fd=240, family=AddressFamily.AF_INET, type=SocketKind.SOCK_STREAM, proto=6, laddr=('192.168
.10.62', 64587), raddr=('192.168.10.66', 7001)> connected to 192.168.10.66:7001: (<_SelectorSocketTransport fd=240 read=
polling write=<idle, bufsize=0>>, <__main__.ClientConnection object at 0x005EBD90>)
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=('192.168.10.62', 64587), raddr=('192.168.10.66', 7001)>
C:\Program Files (x86)\Python36-32\lib\asyncio\selector_events.py:631: ResourceWarning: unclosed transport <_SelectorSoc
ketTransport fd=240>
  source=self)

Second attempt

I did following changes to the code:

  • Removed transport.close() from connection_lost
  • Write some data to connection
  • Added data_received and eof_received callbacks
  • Added more debug logs

Observations:

  • I tried adding transport.close() to connection_made, but it will always result in OSError: [WinError 10038]. Note: This probably another problem, so let's ignore this for now, and assume I won't do this.
  • When writing some data to socket, localhost connection also started to give warnings, but not always.
  • When it gives warning, connection_lost is not called. Why?

Modified code:

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()

Output when it succeeds:

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

Output when it fails (note the lack of 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)
like image 821
user694733 Avatar asked Sep 12 '25 12:09

user694733


1 Answers

Try running the loop a little more. Here is an example using loop.run_forever() and loop.stop(), assuming echo servers on local and remote machines:

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()
like image 52
Udi Avatar answered Sep 14 '25 02:09

Udi