2015-04-24 30 views
5

Sono nuovo di Python 3 e sto giocando con Asyncio. In tal modo, sto vivendo uno strano comportamento con il seguente codice lato server:Perché sto ottenendo un errore ConnectionResetError qui?

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

Quando ho eseguito questo codice cliente (EDIT: codice client viene inserito manualmente in una sessione IPython, il server ha sicuramente tempo per scrivere, prima chiudo la presa) ...

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

... ottengo una traceback errore dal 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 

la questione deve essere in qualche modo in relazione con writer.write, perché quando io chiamo il seguente codice cliente (che rende il server saltare la scrittura), non v'è alcun errore:

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

Il registro del server corrispondente:

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. 

Che cosa mi manca? Sto usando asyncio in modo errato?

Grazie!

risposta

4

Si sta ottenendo l'eccezione perché si sta tentando di scrivere alcuni dati sul client sul lato server, ma il client sta chiudendo il socket immediatamente dopo aver inviato il 'echo', senza effettivamente ricevere la risposta dal server. Se una connessione socket viene chiusa mentre ci sono dati non ricevuti sul filo, si otterrà un errore sul lato di invio, in modo da sapere che il lato remoto potrebbe non aver ricevuto l'ultimo messaggio inviato.

Il problema scompare se si aggiunge una chiamata a socket.recv(1024) sul lato client prima di chiamare socket.close(), in modo che il client attende una risposta dal server prima di chiudere il socket. Si potrebbe anche usare semplicemente un try/except intorno alla chiamata di scrittura sul lato server se si vuole semplicemente gestire l'eccezione, anche quando il client fa la cosa sbagliata.

+0

Dubito che questo sia il caso. Il mio esempio di cliente potrebbe essere un po 'fuorviante. Infatti, tutto il codice cliente è stato inserito manualmente in una sessione IPython (lo modificherò nella domanda). Quindi ci dovrebbe essere abbastanza tempo per il server da scrivere prima che il client sia chiuso. Inoltre, se si osserva da vicino il traceback, l'errore del server si verifica su "readline". – gnargnagnar

+0

@gnargnagnar Provalo; l'errore scompare se si visualizzano i dati scritti dal server. L'errore si verifica se nel socket sono stati scritti dei dati che non sono stati effettivamente ricevuti dal peer al momento della chiusura della connessione, quindi anche se l'operazione 'write' è stata completata, se il peer chiude la connessione senza leggere quei dati , il lato locale riceverà un errore su qualunque operazione di socket sia in esecuzione al momento della sua chiusura. Sono d'accordo che la mia risposta originale non lo abbia chiarito, però. Lo modifico – dano

+0

Sì, hai ragione. Se ricevo l'eco con il client prima della chiusura, non ricevo l'errore del server. C'è qualche ragione per cui il server si preoccupa se il client ha letto tutti i dati? Forse ho una percezione sbagliata delle prese ... – gnargnagnar

Problemi correlati