Your response, while greatly appreciated, wasn't too detailed. I did manage
to find out the solution thanks to your hints though!
I'll post my code, but for the uninitiated:
- I needed to submit a callback method when creating the asyncio.Protocol
class EchoClient; this required defining an __init__() method and calling
the callback in data_received method.
- The callback that was supplied was NOT supposed to be decorated with
coroutine. (that caused me some grief)
- All I needed to do was launch the create_server() coroutine and not
bother with waiting for async coroutine decorated methods in the server
body.
- I added a wait_for() method to servertwo.py in the data_received() method
of the server, so it can handle sleeps (or long method completion times)
- Lastly, the serverone EchoClient class needed to be wrapped in a lambda
to make use of its __init__().
The client.py stayed the same, but I'll post my two finalized files here:
*serverone.py*
#!/usr/bin/env python3.4
import asyncio
class EchoClient(asyncio.Protocol):
message = 'Server One Client Echo'
def __init__(self, callback, *args):
self.callback = callback
def connection_made(self, transport):
self.transport = transport
transport.write(self.message.encode())
print('client data sent: {}'.format(self.message))
def data_received(self, data):
print('client data received: {}'.format(data.decode()))
self.callback(data)
def connection_lost(self, exc):
print('client connection finished')
#asyncio.get_event_loop().stop()
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('server data received: {}'.format(data.decode()))
# Start the call for server two
asyncio.async(self.callservertwo())
@asyncio.coroutine
def callservertwo(self):
echoclient = lambda: EchoClient(self.returncall)
coro = yield from loop.create_connection(
echoclient, '127.0.0.1', 8889)
asyncio.wait_for(coro, 60)
def returncall(self, response):
self.transport.write(response)
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()
*servertwo.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.server_two_says_hello())
asyncio.wait_for(fut, 60)
@asyncio.coroutine
def server_two_says_hello(self):
yield from asyncio.sleep(2)
self.transport.write("Hello - ServerTwo".encode())
# close the socket
self.transport.close()
loop = asyncio.get_event_loop()
coro = loop.create_server(EchoServer, '127.0.0.1', 8889)
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()
On Fri, Sep 5, 2014 at 2:36 PM, Guido van Rossum <[email protected]> wrote:
> The issue is that in server 1 you are trying to mix two paradigms --
> data_received is a protocol callback, but your approach to calling server 2
> is attempting to block. You can create the Future representing the call to
> server 2 in data_receive, but you must define a helper function that writes
> the response and set that helper as the Future's callback.
>
>
> On Fri, Sep 5, 2014 at 2:30 PM, Dann Kettle <[email protected]>
> wrote:
>
>> Hello everyone.
>>
>> I've been working for the past few days on a particular problem that I
>> have been struggling greatly with.
>>
>> In theory, it should be simple:
>>
>> - I have three files: client.py, serverone.py, servertwo.py
>> - I launch both servers in separate terminal windows
>> - I run client.py which then calls serverone.py, the client waits
>> until it receives the server's response, then client returns control to my
>> terminal.
>>
>> That much I have done successfully, as you can read in more detail on my
>> stackoverflow question I made and answered here:
>> http://stackoverflow.com/questions/25691062/how-do-i-get-my-asyncio-client-to-call-a-socket-server-and-waiting-for-response
>>
>> The problem arises when I want my client to call the serverone.py, which
>> in turn calls servertwo.py and waits for its response, before sending
>> the client back servertwo.py's response (through serverone.py). In the
>> real world, I would use a mixture of both server responses to return the
>> result back to client.
>>
>> I got the code working to the point where client calls serverone.py -->
>> serverone.py calls servertwo.py *BUT* ---> client receives the
>> serverone.py response back and --> returns control *before* servertwo
>> responds back to serverone.py.
>>
>> In some cases, client.py just hangs when I try a different approach.
>>
>> servertwo.py DOES communicate to serverone.py and vice versa, the
>> problem here is synchronization I believe. I've been close to pulling my
>> hair out.
>>
>>
>> Any help or opinions are greatly appreciated.
>>
>>
>> Here is the code, I'll describe the problem in more detail below:
>>
>> *client.py*
>>
>> #!/usr/bin/env python3.4
>> import asyncio
>>
>> class EchoClient(asyncio.Protocol):
>> message = 'Client Echo'
>>
>> def connection_made(self, transport):
>> self.transport = 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)
>> result = asyncio.async(coro)
>> asyncio.wait_for(result, 60)
>>
>> loop.run_forever()
>> loop.close()
>>
>>
>> *serverone.py*
>>
>> #!/usr/bin/env python3.4
>> import asyncio
>>
>> class EchoClient(asyncio.Protocol):
>> message = 'Server One Client Echo'
>>
>> def connection_made(self, transport):
>> self.transport = transport
>> self.data = None
>> transport.write(self.message.encode())
>> print('client data sent: {}'.format(self.message))
>>
>> def data_received(self, data):
>> print('client data received: {}'.format(data.decode()))
>> self.data = data.decode()
>>
>> def connection_lost(self, exc):
>> print('client connection finished')
>> #asyncio.get_event_loop().stop()
>>
>> 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('server data received: {}'.format(data.decode()))
>> fut = asyncio.async(self.callservertwo())
>> result = asyncio.wait_for(fut, 60)
>> # "result" is a generator object when it should be the result
>> self.transport.write(str(fut).encode())
>> self.transport.close()
>>
>> @asyncio.coroutine
>> def callservertwo(self):
>> coro = loop.create_connection(
>> EchoClient, '127.0.0.1', 8889)
>> fut = yield from asyncio.async(coro)
>> return asyncio.wait_for(fut, 60)
>>
>> 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()
>>
>> *servertwo.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.server_two_says_hello())
>> asyncio.wait_for(fut, 60)
>>
>> @asyncio.coroutine
>> def server_two_says_hello(self):
>> self.transport.write("Hello - ServerTwo".encode())
>> # close the socket
>> self.transport.close()
>>
>>
>> loop = asyncio.get_event_loop()
>> coro = loop.create_server(EchoServer, '127.0.0.1', 8889)
>> 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()
>>
>> *Problem:*
>>
>> When I run the code, client.py immediately returns. This is what is
>> returned from serverone.py:
>>
>> data received: Task(<callservertwo>)<PENDING>
>>
>> I've tried yield from's, putting things to @asyncio.coroutine decorated
>> methods, but either it tries to return generators, tasks, or client.py
>> hangs.
>>
>>
>> TL;DR: I need to run client.py and have it print out the string "Hello -
>> ServerTwo"
>>
>> Thanks for reading!
>>
>
>
>
> --
> --Guido van Rossum (python.org/~guido)
>