How do I get my asyncio client to invoke a socket server and wait for a response
I am working with a server asyncio.Protocol
where the goal is for the client to invoke the server, but wait until the server responds and data is returned before the client loop stops.
Based on the asyncio doc Echo Client and Server here: https://docs.python.org/3/library/asyncio-protocol.html#protocol-example-tcp-echo-server-and-client , transport.write (. ..) are returned immediately when called.
Through experience, the call is loop.run_until_complete(coroutine)
not made withRuntimeError: Event loop is running.
Execution asyncio.sleep(n)
in a data_received()
server method also has no effect.
yield from asyncio.sleep(n)
and yield from asyncio.async(asyncio.sleep(n))
in data_received()
both the server hang.
My question is, how do I get my client to wait for a server response before giving back control?
source to share
I think to never use a transport / protocol pair directly.
Asyncio has a threading API for high-level programming.
The client code might look like this:
@asyncio.coroutine
def communicate():
reader, writer = yield from asyncio.open_connection(HOST, PORT)
writer.write(b'data')
yield from writer.drain()
answer = yield from reader.read()
# process answer, maybe send new data back to server and wait for answer again
writer.close()
source to share
You don't need to change the client code.
echo-client.py
#!/usr/bin/env python3.4
import asyncio
class EchoClient(asyncio.Protocol):
message = 'Client Echo'
def connection_made(self, transport):
transport.write(self.message.encode())
print('data sent: {}'.format(self.message))
def data_received(self, data):
print('data received: {}'.format(data.decode()))
def connection_lost(self, exc):
print('server closed the connection')
asyncio.get_event_loop().stop()
loop = asyncio.get_event_loop()
coro = loop.create_connection(EchoClient, '127.0.0.1', 8888)
loop.run_until_complete(coro)
loop.run_forever()
loop.close()
The trick is to put your code (including self.transport methods) in a coroutine and use a method wait_for()
, with a statement yield from
before the statement that requires return values, or those that take a to complete:
echo-server.py
#!/usr/bin/env python3.4
import asyncio
class EchoServer(asyncio.Protocol):
def connection_made(self, transport):
peername = transport.get_extra_info('peername')
print('connection from {}'.format(peername))
self.transport = transport
def data_received(self, data):
print('data received: {}'.format(data.decode()))
fut = asyncio.async(self.sleeper())
result = asyncio.wait_for(fut, 60)
@asyncio.coroutine
def sleeper(self):
yield from asyncio.sleep(2)
self.transport.write("Hello World".encode())
self.transport.close()
loop = asyncio.get_event_loop()
coro = loop.create_server(EchoServer, '127.0.0.1', 8888)
server = loop.run_until_complete(coro)
print('serving on {}'.format(server.sockets[0].getsockname()))
try:
loop.run_forever()
except KeyboardInterrupt:
print("exit")
finally:
server.close()
loop.close()
Call echo-server.py
and then echo-client.py
, the client will wait 2 seconds as defined asyncio.sleep
and then stop.
source to share