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

Reply via email to