2

I'm trying to add async functionality via the asyncio library, to a simple TCP server that I'm using. Here is my foobar example:

import os
import sys 
import socket 

class Server(object):
    def __init__(self, addr):
        self.ip, self.port = addr.split(":")
        self.socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        self.socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)


    def listen(self):
        # Listen for incoming requests
        self.socket.bind((self.ip, int(self.port)))
        print("Listening")
        while True:
            try:
                stream, addr = self.socket.accept()
                self._handle_request(stream, addr)
            except socket.timeout:
                continue 

    def _handle_request(self, stream, addr):
        # Here I'm handling the request (read and send response)
        _mssg = self._recv_data(stream)
        pass 

    def _recv_data(self, stream):
        # unpacking the packed struct that was sent, reading 
        # 4-byte struct size first, then using stream.recv()
        # until the entire message has been read
        return 1


if __name__ == "__main__":
    srv = Server("127.0.0.1:9999")
    srv.listen()

I've read the Python docs example here regarding async, but I'm seeing that this example is using the built-in server functionality of the asyncio library. Could anyone help me generalize this to my simple TCP server? I need the flexibility of a custom solution (but still the functionality of the linked async server)

1 Answer 1

2

If my understanding is correct, you are looking for guidance how to adapt existing TCP code to asyncio. You will definitely need to make substantial changes to the code, but the changes should be fairly straightforward.

For example, the code from the question could be adapted to asyncio as follows (untested):

import asyncio, struct

class Server(object):
    def __init__(self, addr):
        self.ip, self.port = addr.split(":")

    async def listen(self):
        # Listen for incoming requests
        asrv = await asyncio.start_server(self._handle_request, self.ip, int(self.port))
        print("Listening")
        # on Python 3.6 you would use something like
        # while True: await asyncio.sleep(1000)
        await asrv.serve_forever()

    async def _handle_request(self, reader, writer):
        addr = writer.get_extra_info('peername')
        # Here I'm handling the request (read and send response)
        print('handling', addr)
        msg = await self._recv_data(reader)
        # ...

    async def _recv_data(self, stream):
        size_msg = await stream.readexactly(4)
        size, = struct.unpack('l', size_msg)
        msg = await stream.readexactly(size)
        return msg

if __name__ == "__main__":
    srv = Server("127.0.0.1:9999")
    asyncio.get_event_loop().run_until_complete(srv.listen())
Sign up to request clarification or add additional context in comments.

2 Comments

It seems two mistakes in your code. You are using struct.unpack but you imported socket. And lacking of async in front of _handle_request. :)
@kcorlidy Thanks for the careful reading, I've fixed both errors and marked the code as untested. The code was meant primarily as proof of concept to show the kind of code that should be written, but that is of course no excuse for sloppiness. :)

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.