2015-04-24 12 views
5

Jestem nowy w Pythonie 3 i bawię się z asyncio. W ten sposób, jestem przeżywa dziwne zachowanie z następującego kodu po stronie serwera:Dlaczego otrzymuję tutaj błąd 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() 

Kiedy uruchomić ten kod klienta (EDIT: Kod klient jest wprowadzany ręcznie do sesji ipython serwer pewno ma czasu na pisanie przed I zamknąć gniazdo) ...

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

... otrzymuję traceback błędu z serwera:

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 

problem musi być w jakiś sposób w związku z writer.write, ponieważ gdy zgłoszę następujący kod klienta (co sprawia, że ​​serwer pominąć pisanie), nie ma błędu:

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

Odpowiedni dziennik serwera:

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. 

Czego mi brakuje? Czy używam asyncio niepoprawnie?

Dzięki!

Odpowiedz

4

Otrzymujesz wyjątek, ponieważ próbujesz zapisać niektóre dane z powrotem do klienta po stronie serwera, ale klient zamyka socket natychmiast po wysłaniu 'echo', bez otrzymywania odpowiedzi od serwer. Jeśli połączenie z gniazdem zostanie zamknięte, gdy na drucie pojawią się nieodczytane dane, pojawi się błąd po stronie wysyłającej, dzięki czemu wiesz, że strona zdalna mogła nie odebrać tego, co ostatnio wysłałeś.

Problem znika, jeśli przed wywołaniem numeru socket.close() zostanie dodane połączenie z numerem klienta, aby klient oczekiwał na odpowiedź z serwera przed zamknięciem gniazda. Można także po prostu użyć polecenia try/ wokół wywołania zapisu po stronie serwera, jeśli chcesz po prostu obsługiwać wyjątek, nawet jeśli klient robi coś niewłaściwego.

+0

Wątpię, czy tak jest w tym przypadku. Przykład mojego klienta może być trochę mylący. W rzeczywistości cały kod klienta został ręcznie wprowadzony do sesji IPython (będę edytować to w pytaniu). Powinien więc wystarczyć czas, aby serwer zapisał, zanim klient zostanie zamknięty. Ponadto, jeśli przyjrzymy się dokładnie ścieżce zwrotnej, błąd serwera pojawi się w "readline". – gnargnagnar

+0

@gnargnagnar Spróbuj; błąd zniknie, jeśli "recv" dane zapisane przez serwer. Błąd występuje, jeśli dane są zapisane w gnieździe, które nie zostały faktycznie odebrane przez partnera w czasie zamykania połączenia - tak więc nawet jeśli operacja 'write' została zakończona, jeśli partner zamknie połączenie bez odczytu tych danych , strona lokalna otrzyma błąd, niezależnie od tego, jaka operacja gniazda działa w momencie zamknięcia. Zgadzam się, że moja pierwotna odpowiedź nie wyjaśniła tego. Będę to edytować. – dano

+0

Tak, masz rację. Jeśli wycofam echo z klientem przed zamknięciem, nie otrzymam błędu serwera. Czy istnieje jakikolwiek powód, dla którego serwer dba o to, czy klient odczytał wszystkie dane? Może mam złe postrzeganie gniazd. – gnargnagnar