miguelgrinberg / python-socketio

Python Socket.IO server and client
MIT License
3.96k stars 587 forks source link

Unable to create server & client in same process #904

Closed asedlack closed 2 years ago

asedlack commented 2 years ago

Describe the bug Unable to reproduce scenario described here: https://github.com/miguelgrinberg/python-socketio/issues/211#issuecomment-434490546

You can also have a server and a client in the same process if that helps.

To Reproduce

import asyncio
import socketio
from aiohttp import web

server = socketio.AsyncServer()
client = socketio.AsyncClient()
app = web.Application()
server.attach(app)

async def main():
    await client.connect('localhost:3001')
    web.run_app(app, port=3000)

if __name__ == '__main__':
    asyncio.run(main())

Expected behavior Start socketio server on port 3000 in the same process as a socketio client connected to a different server on port 3001.

Logs Produces following error: RuntimeError: Cannot run the event loop while another loop is running

miguelgrinberg commented 2 years ago

This is actually a bug in your application. You created your own async loop. The Socket.IO package is happy enough to use it, but aiohttp defaults to use its own loop, unless you explicitly provide your own. The Application class takes a loop argument that might work (https://docs.aiohttp.org/en/stable/web_reference.html#aiohttp.web.Application). Even if that does not work, this isn't a problem with this package, so please follow up with aiohttp.

asedlack commented 2 years ago

Thanks for the pointer. Turns out the loop argument for Application is deprecated, but that got me on the right track. In case anyone comes across this later, I had luck with this strategy. Since aiohttp.web.run_app is blocking, there is an alternative to attach the socketio server to the existing async loop.

import asyncio
import socketio
from aiohttp import web

server = socketio.AsyncServer()
client = socketio.AsyncClient()

async def main():
    await client.connect('localhost:3001')

    app = web.Application()
    server.attach(app)

    runner = web.AppRunner(app)
    await runner.setup()
    site = web.TCPSite(runner, host='0.0.0.0', port=3000)
    await site.start()
    await io_client.wait()

if __name__ == '__main__':
    asyncio.run(main())