3

I have the example echo server

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):
        self.transport.write(data)
        # Client piece goes here

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

What I'm trying to do is add a client piece where I've commented that will connect to a new server and send the data off that-a-way. There's the echo client, but I need a process that looks like this:

    +-----------+    +-----------+    +--------------+
    | My Server |    | My Client |    | Other Server |
    +-----------+    +-----------+    +--------------+
          |                |                 |
 ===>Get some data         |                 |
          |                |                 |
      Send data ---------->|                 |
          |                |                 |
          |            Send data ----------->|
          |                |                 |
          |                |              Do Stuff
          |                |                 |
          |                | <-----------Send Data
          |                |                 |
          | <--------- Send data             |
          |                |                 |
 <=== Send data            |                 |
          |                |                 |
          |                |                 |
          |                |                 |
          |                |                 |

Obviously I can do this synchronously, but I'm trying to make the client -> other server bit async, and I'm not really figuring out how to use the asyncio methods to communicate between my server piece and a client piece.

What do I need to do here?

1 Answer 1

12

Here is a simple proxy which allow you to wget 127.0.0.1:8888 and get a html response from google:

import asyncio

class Client(asyncio.Protocol):

    def connection_made(self, transport):
        self.connected = True
        # save the transport
        self.transport = transport

    def data_received(self, data):
        # forward data to the server
        self.server_transport.write(data)

    def connection_lost(self, *args):
        self.connected = False

class Server(asyncio.Protocol):
    clients = {}
    def connection_made(self, transport):
        # save the transport
        self.transport = transport

    @asyncio.coroutine
    def send_data(self, data):
        # get a client by its peername
        peername = self.transport.get_extra_info('peername')
        client = self.clients.get(peername)
        # create a client if peername is not known or the client disconnect
        if client is None or not client.connected:
            protocol, client = yield from loop.create_connection(
                Client, 'google.com', 80)
            client.server_transport = self.transport
            self.clients[peername] = client
        # forward data to the client
        client.transport.write(data)

    def data_received(self, data):
        # use a task so this is executed async
        asyncio.Task(self.send_data(data))

@asyncio.coroutine
def initialize(loop):
    # use a coroutine to use yield from and get the async result of
    # create_server
    server = yield from loop.create_server(Server, '127.0.0.1', 8888)

loop = asyncio.get_event_loop()

# main task to initialize everything
asyncio.Task(initialize(loop))

# run
loop.run_forever()
Sign up to request clarification or add additional context in comments.

5 Comments

Why? I've already used this kind of references. Any traceback or useful error message...?
Exception in callback <bound method _SelectorSocketTransport._read_ready of <asyncio.selector_events._SelectorSocketTransport object at 0x7f1c5d10e198>> () Traceback (most recent call last): File "/usr/local/lib/python3.4/asyncio/events.py", line 38, in _run self._callback(*self._args) File "/usr/local/lib/python3.4/asyncio/selector_events.py", line 486, in _read_ready self._protocol.data_received(data) File "the_test.py", line 9, in data_received self.client.transport.write(data) AttributeError: 'Server' object has no attribute 'client'
Upon further examination of the asyncio docs, the value yielded as server is not actually an instance of Server - it's some kind of wrapped(??) value
You were right. It's not that simple. My new example works for me. Hope it will put you on the way.
Nice! That seems to work, should get me headed in the right direction.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.