gpt4 book ai didi

python aiohttp websockets关闭浏览器选项卡处理

转载 作者:IT王子 更新时间:2023-10-29 06:09:30 26 4
gpt4 key购买 nike

我正在尝试使用 aiohttp WebSockets 和 aioredis 进行存储来创建简单的活跃用户计数器。当我在 Google Chrome 中添加新标签时,我的计数器在所有已打开的标签中完美递增。但是,当我关闭一个选项卡时,其他选项卡中没有任何变化。

我想我应该在整个 async/await 机制中遗漏一些东西,但找不到可能出错的地方。

这是我的应用

import asyncio

import aiohttp
from aiohttp import web
import aioredis


class CounterView(web.View):
async def get(self):
request = self.request
app = request.app

ws = web.WebSocketResponse()

app['websockets'].append(ws)
await ws.prepare(request)

count = int(await app['db'].incr('counter'))
for ws in app['websockets']:
await ws.send_json({'msg': {'count': count}})

async for msg in ws:
if msg.type == aiohttp.WSMsgType.TEXT:
await ws.send_str(msg.data)
elif msg.type == aiohttp.WSMsgType.ERROR:
print('ws connection closed with exception %s' %
ws.exception())
app['websockets'].remove(ws)

# Execution stops here (on await app['db'] ...) and never returns
count = int(await app['db'].decr('counter'))
for ws in app['websockets']:
await ws.send_json({'msg': {'count': count}})
return ws


async def init_app(loop):
app = web.Application(loop=loop)
db = await aioredis.create_redis('redis://localhost', loop=loop)
app['db'] = db
app['websockets'] = []
app.add_routes([
web.get('', CounterView),
])
return app

if __name__ == '__main__':
loop = asyncio.get_event_loop()
web.run_app(init_app(loop))

和index.html模板

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
How many people seeing this page now: <span id="counter"></span>
</body>
<script>
window.onload = function () {
const ws = new WebSocket('ws://localhost:8080');
ws.onmessage = function (event) {
const data = JSON.parse(event.data);
let span = document.getElementById('counter');
console.log(data.msg.count);
span.innerHTML = data.msg.count;
}
};
</script>
</html>

我也在 Firefox 中尝试过,那里发生了一些非常奇怪的事情。

打开了两个选项卡,在两个选项卡上都得到了 counter = 2。然后先重新加载 - 在其中得到 1,在第二个中仍然是 2。再次重新加载第一个选项卡 - 得到 2。此后,每次重新加载都会得到 2

直到我重新加载第二个选项卡 - 相同的过程(重新加载 - 1 - 重新加载 - 2 发生在那里并在第一个选项卡中重复)

我也尝试申请 https://stackoverflow.com/a/48695448/6627564这个答案,但没有任何改变。

调试显示代码执行了 count = int(await app['db'].decr('counter')) 然后跳到某处永远不会返回。

非常感谢任何帮助。据我所知,事件循环应该在这一行之后返回执行。也许协程以某种方式被破坏了,但我没有在库中找到任何执行此操作的代码。

我的问题与 Python Asyncio Websocket not detecting a disconnect on wifi but does on localhost 中描述的不同

首先,我的连接都在本地主机上。

其次,async for msg in ws循环之后的代码才真正开始执行,调试显示确实调用了ws.close()方法。但是在下一个 await 上有一个上下文切换,并且执行不会继续。

我也尝试过使用 ws = web.WebSocketResponse(heartbeat=1.0) 激活 ping-pong,但我在 Dev Tools 中看不到任何消息。我在 await ws.prepare(request) 之后添加了单个 await ws.ping() ,不幸的是,Dev Tools 中没有出现任何消​​息。这里肯定出了问题......

最佳答案

对于任何对此问题感兴趣的人 - 解决方案。

此代码中存在三个问题)。其中两个实际上与 asyncio 无关。

首先,app['websockets']list 并且由于某种原因 remove(ws) 无法找到正确的 WebSocketResponse 实例并从列表中删除另一个 WebSocketResponse。解决方案是使用 set() 而不是 list 来存储事件的 websocket。这是因为set.discard()使用了__hash__魔术方法,而list.remove()使用了__eq__方法.不幸的是,我在 WebSocketResponse 中找不到 __eq__ 的实现细节,但是 __hash__ 使用内置的 id 函数保证正确的工作。

其次,看这几行

ws = web.WebSocketResponse()
....
......
for ws in app['websockets']:
await ws.send_json({'msg': {'count': count}})

局部变量 wsfor 循环中被覆盖。解决方案是只使用其他变量名进行迭代,如 other_ws

第三个在aiohttp 的文档中有描述Web Handler Cancellation .

它声明在每次 await 调用时,如果客户端已断开连接,则可以终止处理程序。情况确实如此 - 在断开连接后的第一个 await 上,我的处理程序死了。文档中也提供了解决方案,我决定使用 asyncio.shield.

关于python aiohttp websockets关闭浏览器选项卡处理,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/50318875/

26 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com