I am new to Python 3 and am playing around with asyncio. Thereby, I am experiencing a strange behavior with the following server-side code:
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()
When I run this client code (EDIT: client code is entered manually into an IPython session, the server definitely has time to write before I close the socket)...
import socket
client = socket.create_connection(('localhost', 8888))
client.sendall('echo\n'.encode())
client.close()
... I get an error traceback from the 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
The issue must be somehow in relation with writer.write
, because when I call the following client code (which makes the server skip the writing), there is no error:
import socket
client = socket.create_connection(('localhost', 8888))
client.sendall('foo\n'.encode())
client.close()
The corresponding 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.
What am I missing? Am I using asyncio incorrectly?
Thanks!
You're getting the exception because you're trying to write some data back to the client on the server-side, but the client is closing the socket immediately after sending in the 'echo' , without actually receiving the response from the server.
exception ConnectionResetError. A subclass of ConnectionError , raised when a connection is reset by the peer. Corresponds to errno ECONNRESET . exception FileExistsError. Raised when trying to create a file or directory which already exists.
You're getting the exception because you're trying to write some data back to the client on the server-side, but the client is closing the socket
immediately after sending in the 'echo'
, without actually receiving the response from the server. If a socket connection is closed while there is unreceived data on the wire, you'll get an error on the sending side, so that you know the remote side may not have received whatever you last sent.
The problem goes away if you add a call to socket.recv(1024)
on the client-side prior to calling socket.close()
, so that the client waits for a response from the server before closing the socket. You could also just use a try
/except
around the write call on the server-side if you just want to gracefully handle the exception, even when the client does the wrong thing.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With