2015-04-24 3 views
5

Ich bin neu in Python 3 und spiele mit asyncio herum. Dabei erlebe ich ein seltsames Verhalten mit dem folgenden serverseitigen Code:Warum erhalte ich hier einen ConnectionResetError?

import asyncio 


@asyncio.coroutine 
def handle_client(reader, writer): 
    print('Client connected.') 
    client_connected = True 
    while client_connected: 
     print('Waiting for client event.') 
     line = yield from reader.readline() 
     if line: 
      print('Got: {}'.format(line)) 
      if line.decode() == 'echo\n': 
       print('Sending back echo.') 
       writer.write(line) 
      else: 
       print('Not sending back anything.') 
     else: 
      print('Client disconnected.') 
      client_connected = False 


if __name__ == '__main__': 
    asyncio.async(asyncio.start_server(handle_client, 'localhost', 8888)) 
    asyncio.get_event_loop().run_forever() 

Wenn ich diesen Client Code ausführen (EDIT: Client-Code wird manuell in eine IPython Sitzung eingegeben, der Server hat auf jeden Fall Zeit zu schreiben, bevor ich schließe die Buchse) ...

import socket 
client = socket.create_connection(('localhost', 8888)) 
client.sendall('echo\n'.encode()) 
client.close() 

... ich erhalte eine Fehlerrückverfolgung von dem Server:

C:\Users\Gnar\Anaconda3\python.exe C:/Users/Gnar/Code/echo.py 
Client connected. 
Waiting for client event. 
Got: b'echo\n' 
Sending back echo. 
Waiting for client event. 
Task exception was never retrieved 
future: <Task finished coro=<handle_client() done, defined at C:/Users/Gnar/Code/echo.py:4> exception=ConnectionResetError(10054, 'An existing connection was forcibly closed by the remote host', None, 10054, None)> 
Traceback (most recent call last): 
    File "C:\Users\Gnar\Anaconda3\lib\asyncio\tasks.py", line 234, in _step 
    result = coro.throw(exc) 
    File "C:/Users/Gnar/Code/echo.py", line 10, in handle_client 
    line = yield from reader.readline() 
    File "C:\Users\Gnar\Anaconda3\lib\asyncio\streams.py", line 425, in readline 
    yield from self._wait_for_data('readline') 
    File "C:\Users\Gnar\Anaconda3\lib\asyncio\streams.py", line 393, in _wait_for_data 
    yield from self._waiter 
    File "C:\Users\Gnar\Anaconda3\lib\asyncio\futures.py", line 386, in __iter__ 
    yield self # This tells Task to wait for completion. 
    File "C:\Users\Gnar\Anaconda3\lib\asyncio\tasks.py", line 287, in _wakeup 
    value = future.result() 
    File "C:\Users\Gnar\Anaconda3\lib\asyncio\futures.py", line 275, in result 
    raise self._exception 
    File "C:\Users\Gnar\Anaconda3\lib\asyncio\selector_events.py", line 662, in _read_ready 
    data = self._sock.recv(self.max_size) 
ConnectionResetError: [WinError 10054] An existing connection was forcibly closed by the remote host 

die Frage muss irgendwie in Beziehung mit writer.write, weil, wenn ich den folgenden Client-Code aufrufen (die der Server überspringen das Schreiben macht), kein Fehler vorhanden ist:

import socket 
client = socket.create_connection(('localhost', 8888)) 
client.sendall('foo\n'.encode()) 
client.close() 

Der entsprechende Server-Log:

C:\Users\Gnar\Anaconda3\python.exe C:/Users/Gnar/Code/echo.py 
Client connected. 
Waiting for client event. 
Got: b'foo\n' 
Not sending back anything. 
Waiting for client event. 
Client disconnected. 

Was bin ich? Verwende ich Asyncio falsch?

Danke!

Antwort

4

Sie die Ausnahme, wenn es darum, weil Sie einige Daten zurück an den Client auf der Server-Seite zu schreiben sind versucht, aber der Kunde die socket sofort enden im 'echo' nach dem Senden, ohne tatsächlich die Antwort von dem Empfang Server. Wenn eine Socket-Verbindung geschlossen wird, während nicht empfangene Daten auf der Leitung vorhanden sind, erhalten Sie auf der Sendeseite einen Fehler, sodass Sie wissen, dass die Gegenstelle möglicherweise nicht das empfangen hat, was Sie zuletzt gesendet haben.

Das Problem verschwindet, wenn Sie einen Anruf socket.recv(1024) auf der Client-Seite vor dem Aufruf socket.close() hinzufügen, so dass der Client auf eine Antwort vom Server wartet, bevor der Socket geschlossen wird. Sie könnten auch einfach einen try/except um den Schreibaufruf auf der Serverseite verwenden, wenn Sie nur die Ausnahme elegant handhaben möchten, selbst wenn der Client die falsche Sache tut.

+0

Ich bezweifle, dass dies der Fall ist. Mein Client-Beispiel kann ein bisschen irreführend sein. In der Tat wurde der gesamte Client-Code manuell in eine IPython-Sitzung eingegeben (ich werde dies in der Frage bearbeiten). Daher sollte genügend Zeit für den Server vorhanden sein, um zu schreiben, bevor der Client geschlossen wird. Wenn Sie sich das Traceback genauer ansehen, tritt der Serverfehler bei "readline" auf. – gnargnagnar

+0

@gnargnagnar Probieren Sie es aus; Der Fehler verschwindet, wenn Sie die vom Server geschriebenen Daten abrufen. Der Fehler tritt auf, wenn Daten in den Socket geschrieben werden, die zum Zeitpunkt des Verbindungsabbruchs vom Peer nicht tatsächlich empfangen wurden - also auch dann, wenn die 'write'-Operation abgeschlossen ist, wenn der Peer die Verbindung schließt, ohne diese Daten zu lesen , die lokale Seite erhält einen Fehler bei der Socket-Operation, die gerade ausgeführt wird, wenn sie geschlossen ist. Ich stimme zu, dass meine ursprüngliche Antwort dies jedoch nicht klarstellte. Ich werde es bearbeiten. – dano

+0

Ja, Sie haben Recht. Wenn ich das Echo vor dem Schließen mit dem Client einlese, erhalte ich den Serverfehler nicht. Gibt es einen Grund, warum der Server sich darum kümmert, ob der Client dann alle Daten ausgelesen hat? Vielleicht habe ich eine falsche Vorstellung von Steckdosen .. – gnargnagnar