1

我有一个 python websocket 服务器试图与一个 javascript websocket 客户端(嵌入在 HTML 中)进行通信。事件会emit立即从服务器编辑,但服务器发送事件触发器需要超过 30 秒,尽管客户端和服务器都在本地托管。

这是服务器的相关代码:

sio = socketio.AsyncServer(cors_allowed_origins='*')
app = web.Application() #aiohttp web server
loop = asyncio.get_event_loop()
sio.attach(app)

async def index(request):
    with open('./index.html') as f:
        return web.Response(text=f.read(), content_type='text/html')

app.router.add_get('/', index)
app.router.add_get('/index.html', index)

if __name__ == '__main__':
    web.run_app(app)

事件是这样触发的(编辑,这必须通过事件循环来完成,emit从同步函数运行的异步函数也是如此。):

print('Starting event')
loop = asyncio.new_event_loop()
asyncio.set_event_loop(loop)
loop.run_until_complete(sio.emit('ChangeProgressState'))
loop.close()
print('Event has been fired.')

但是,打印语句会立即显示。在客户端,我正在连接并尝试使用这样的事件:

const socket = io.connect("http://localhost:8080", {
      transports: ['websocket']
})

socket.on("ChangeProgressState", function (data) {
    console.log("got event.")
    //some code here...
});

但是,从触发事件到 javascript 套接字注意到的时间可能很长,从 30 秒到有时几分钟。我在这里做错了吗?

应该注意的是,消耗的资源非常少(2%-5%)(内存和 CPU),所以我目前认为这不是问题。任何帮助将非常感激。

编辑 2019 年 11 月 15 日:我尝试查看应用程序的网络选项卡(树莓派上的铬浏览器)。它似乎显示了初始套接字连接,但它没有显示任何套接字之间的通信,即使在事件最终触发之后也是如此。

编辑2:这绝对是服务器端的问题。我基本上可以立即将事件从 JS 客户端发送到 python 服务器,但反之则需要很长时间才能到达。我不太清楚为什么。

4

2 回答 2

0

啊,好吧,所以我的直觉说这听起来客户正在长时间轮询。许多套接字库首先建立长轮询,然后升级到 ws 连接。

看了Socket.io 之后

...它首先建立一个长轮询连接,然后尝试升级到更好的“测试”在侧面的传输,比如 WebSocket。...

所以我不相信你做错了什么,只是建立WebSocket连接的初始化过程。

至于 python 部分,老实说,这对我来说有点模糊。我的第一个猜测是循环代码不会阻止执行 print 语句——但我对 JavaScript 比对 Python 更熟悉,所以在这方面并不完全确定。我的第二个猜测是,我确实从其他 pub/sub 库中知道,服务器端引擎有时会使用中间层(有时是缓存,有时是队列)来帮助确保发送/接收消息,这也是一种可能性.

额外花絮:我怀疑如果您查看浏览器开发工具的网络选项卡,它会显示该行为、某种形式的 HTTP 请求,然后最终您会看到套接字连接。尝试关闭/打开 Python 服务器/服务也将展示 socket.io 在浏览器中的稳健性,以及它在与各种互联网通信协议进行通信时如何处理不稳定网络的边缘情况。

于 2019-11-14T21:55:03.027 回答
0

感谢所有帮助回答这个问题的人!我终于找到了一个有点非正统的解决方案,所以我将在这里解释整个情况。

本质上,为了在同步上下文中运行异步方法,您必须run_until_complete在事件循环上使用异步方法。当我被问到这个问题时,我就是这样做的。但是,在与 python-socketio 库的创建者交谈后,您似乎必须在与服务器运行的事件循环相同的事件循环中运行它。

但是,这会产生不同的问题。如果一个事件循环已经在运行,python 不允许你在它上面使用 run_until_complete,给你一个错误:RuntimeError: This event loop is already running.

那么,这件事听起来很矛盾吧?你是对的。但是,这个问题非常普遍,以至于存在另一个库,其唯一目的是对 python asyncio 库进行猴子修补以解决此问题。我在这里找到了这个库。

安装并使用该库后,我现在可以执行此操作,这完全解决了我的问题:

main_event_loop = asyncio.get_event_loop()
main_event_loop.run_until_complete(sio.emit("ChangeProgressState"))

现在程序按预期运行,消息立即发送/到达。

于 2019-11-18T17:11:48.967 回答