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?

+3


source to share


2 answers


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

      

+3


source


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.

+3


source







All Articles