2017-01-17 3 views
2

私はasyncioクライアントを使用して接続し、サーバーから切断します。非同期クライアント: `connection_lost`が呼び出されず、ResourceWarningが発生します

  • 同じコンピュータ上のサーバープログラムに接続すると、接続が正常に終了します。追加:接続にデータを書き始めると、この接続でも警告が表示され始めた時には。下記の第2版のバージョンを参照してください。

  • ローカルネットワーク上のデバイスに接続すると、閉鎖されていないトランスポート用にResourceWarningが得られます。

接続を正しく閉じるにはどうすればよいですか?

私はWindows 7(64ビット)でPython 3.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() 

コンソール出力:

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) 

第二の試み

私は、コードに変更を次のようでした:

  • connection_lost
  • からtransport.close()を削除し、接続
  • 追加data_receivedeof_receivedコールバック
  • さらに追加のデバッグログ

観察にいくつかのデータを書き込みます。

  • 私はconnection_madetransport.close()を追加してみましたしかし、それは常にresulされますtはOSError: [WinError 10038]です。 注:これは恐らく別の問題ですので、今はこれを無視して、私がこれをしないと仮定しましょう。
  • ソケットにデータを書き込むときに、ローカルホスト接続も警告を出すようになりましたが、必ずしもそうとは限りません。
  • 警告を発すると、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) 
+0

)は(transport.close :-) Linux上で 'WinError'を再現することはできません正常に動作します:ここでは、ローカルとリモートマシン上echo server秒を想定しloop.run_forever()loop.stop()を、使用した例があります。 '.close()'の前に 'time.sleep(0.05)'を追加してみてください。 – Udi

+0

@Udiタイムアウトは役に立たないようですが、Windowsは何かについて気になるようです。私はおそらくそれについて別の質問をするでしょう。 *しかし、遅延だけを使用すると元の問題を防ぐことができます。私はいくつかのテストをする必要がありますが、私のプログラムではすべてが速すぎるかもしれません。これまでのご協力ありがとうございます。 – user694733

+0

明らかに、ウィンドウ 'write_eof'は' close() 'を呼び出します。また、ループを永遠に実行し、 'connection_lost'で' stop() 'を実行してみてください。 – Udi

答えて

1

をどちらかを待ちます接続を閉じるためのサーバー、またはを使用するを押して輸送を終了します。また、これは(!connection_lostからtransport.close()を呼び出さないでください)connection_lostをトリガーします:

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!')を追加するか、ローカルサーバーの応答が遅くなるようにしてください。

+0

あなたはローカルホストソケットにデータを送信する権利を持っていました。警告も発生します。しかし、私は 'connection_made'からトランスポートを閉じることはできません。OSErrorsが始まります。私はその問題を修正した。 – user694733

+0

これは新しい質問です。 – Udi

+0

OSErrorはおそらく別の質問に行く必要がありますが(私はそれを除外するように編集します)、この質問はまだ開いたままです:時には 'connection_lost'が呼び出されないので、 。 – user694733

1

ループをもう少し実行してみてください。

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() 
関連する問題