Quantcast
Channel: Active questions tagged python - Stack Overflow
Viewing all articles
Browse latest Browse all 16832

How can I convert a Python multithreaded socket client/server to asyncio?

$
0
0

I'm writing a tool to test an asyncio based server end-to-end. Initially I was going to spin up the server in one terminal window, and run the test in a separate window, but then I realized that it should be possible to just do that in one script. After all, I can do it with a concurrent.futures.ThreadPoolExecutor, but I'm struggling converting the logic using await/async def.

Here's a working example using the TPE:

import argparseimport socketimport concurrent.futuresimport threadingimport socketserverclass TCPHandler(socketserver.BaseRequestHandler):    def handle(self):        print(f'Got data: {self.request.recv(1024).strip().decode()}')def started_server(*, server):    print('starting server')    server.serve_forever()    print('server thread closing')def run_client(*, host, port, server):    print('client started, attempting connection')    with socket.create_connection((host, port), timeout=0.5) as conn:        print('connected')        conn.send(b'hello werld')    print('closing server')    server.shutdown()    print('cancelled')def test_the_server(*, host, port):    ex = concurrent.futures.ThreadPoolExecutor(max_workers=3)    print('server a')    quitter = threading.Event()    server = socketserver.TCPServer((host, port), TCPHandler)    a = ex.submit(started_server, server=server)    b = ex.submit(run_client, host=host, port=port, server=server)    print(a.result(), b.result())    print('server b')def do_it():  # Shia LeBeouf!    parser = argparse.ArgumentParser(usage=__doc__)    parser.add_argument("--host", default="127.0.0.1")    parser.add_argument("-p", "--port", type=int, default=60025)    args = parser.parse_args()    exit(test_the_server(host=args.host, port=args.port))if __name__ == "__main__":    do_it()

How would I convert this to use an asyncio loop? I'm pretty sure that I need to spawn the server asyncio loop in a thread, but so far it's just turned out blocking, and other questions on SO have failed to provide a solution (or have been outdated).

Here's an example of something that fails for me:

import asyncioimport argparseimport socketimport concurrent.futuresimport threadingimport socketserverclass EchoHandler(asyncio.Protocol):    def data_received(self, data):        print(f"Got this data: {data.decode()}")async def run_server(*, server):    print('starting server')    server = await server    async with server:        print('start serving')        await server.start_serving()        print('waiting on close')        await server.wait_closed()    print('server coro closing')def started_server(*, server):    print('server thread started')    asyncio.run(run_server(server=server))    print('server thread finished')def run_client(*, host, port, server):    print('client started, attempting connection')    with socket.create_connection((host, port), timeout=0.5) as conn:        print('connected')        conn.send(b'hello werld')    print('closing server')    server.close()    print('cancelled')async def fnord(reader, writer):    data = await reader.read(100)    message = data.decode()    print('got', message)def test_the_server(*, host, port):    ex = concurrent.futures.ThreadPoolExecutor(max_workers=3)    print('server a')    quitter = threading.Event()    #server = socketserver.TCPServer((host, port), TCPHandler)    server = asyncio.start_server(fnord, host, port)    a = ex.submit(started_server, server=server)    b = ex.submit(run_client, host=host, port=port, server=server)    print(a.result(), b.result())    print('server b')def do_it():  # Shia LeBeouf!    parser = argparse.ArgumentParser(usage=__doc__)    parser.add_argument("--host", default="127.0.0.1")    parser.add_argument("-p", "--port", type=int, default=60025)    args = parser.parse_args()    exit(test_the_server(host=args.host, port=args.port))if __name__ == "__main__":    do_it()

I was hoping that https://docs.python.org/3/library/asyncio-eventloop.html#asyncio.Server.wait_closed would be enough that when I call server.close() on the other thread that it would shut down the server, but it doesn't appear to be the case. serve_forever behaves the same as the start_serving approach.


Viewing all articles
Browse latest Browse all 16832

Trending Articles



<script src="https://jsc.adskeeper.com/r/s/rssing.com.1596347.js" async> </script>