python-3.x - Python websockets 和 gtk - 对 asyncio 队列感到困惑

标签 python-3.x websocket gtk3 python-asyncio

我是Python异步编程的新手,我正在尝试编写一个脚本来启动WebSocket服务器,监听消息,并在触发某些事件(例如按's'键)时发送消息一个 gtk 窗口。这是我到目前为止所拥有的:

import gi
gi.require_version('Gtk', '3.0')
from gi.repository import Gtk
import asyncio
import websockets
import threading

ws = None

async def consumer_handler(websocket, path):
    global ws
    ws = websocket
    await websocket.send("Hello client")
    while True:
        message = await websocket.recv()
        print("Message from client: " + message)

def keypress(widget,event):
    global ws
    if event.keyval == 115 and ws: #s key pressed, connection open
        asyncio.get_event_loop().create_task(ws.send("key press"))
        print("Message to client: key press")

def quit(widget):
    Gtk.main_quit()

window = Gtk.Window(Gtk.WindowType.TOPLEVEL)
window.connect("destroy", quit)
window.connect("key_press_event", keypress)
window.show()

start_server = websockets.serve(consumer_handler, 'localhost', 8765)
asyncio.get_event_loop().run_until_complete(start_server)
wst = threading.Thread(target=asyncio.get_event_loop().run_forever)
wst.daemon = True
wst.start()

Gtk.main()

这是客户端网页:

<!DOCTYPE html>
<html>
    <head>
        <title>Websockets test page</title>
        <meta charset="UTF-8" />
        <script>
var exampleSocket = new WebSocket("ws://localhost:8765");

function mylog(msg) {
    document.getElementById("log").innerHTML += msg + "<br/>";
}

function send() {
    mylog("Message to server: Hello server");
    exampleSocket.send("Hello server"); 
}

exampleSocket.onopen = function (event) {
    mylog("Connection opened");
};

exampleSocket.onmessage = function (event) {
    mylog("Message from server: " + event.data);
}
        </script>
    </head>
    <body>
        <p id="log"></p>
        <input type="button" value="Send message" onclick="send()"/>
    </body>
</html>

运行 python 代码,然后在浏览器中加载网页,现在浏览器发送的任何消息都会显示在 python stdout 中,到目前为止一切顺利。但是,如果您在 gtk 窗口中按“s”键,python 不会发送消息,直到从浏览器收到另一条消息(通过按“发送消息”按钮)。我认为 await websocket.recv() 的意思是将控制权返回到事件循环,直到收到消息?如何让它在等待接收时发送消息?

最佳答案

But if you hit the 's' key in the gtk window, python doesn't send the message until another message is received from the browser

问题出在这一行:

asyncio.get_event_loop().create_task(ws.send("key press"))

由于asyncio事件循环和GTK主循环在不同的线程中运行,因此您需要使用run_coroutine_threadsafe将协程提交给 asyncio。像这样的东西:

asyncio.run_coroutine_threadsafe(ws.send("key press"), loop)

create_task 将协程添加到可运行协程队列中,但无法唤醒事件循环,这就是为什么您的协程仅在 asyncio 中发生其他情况时才运行。此外,create_task 不是线程安全的,因此在事件循环本身修改运行队列时调用它可能会损坏其数据结构。 run_coroutine_threadsafe 没有这些问题,它安排事件循环尽快唤醒,并使用互斥锁来保护事件循环的数据结构。

关于python-3.x - Python websockets 和 gtk - 对 asyncio 队列感到困惑,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/49851514/

相关文章:

c++11 - 如何在 Gtk::DrawingArea 区域绘制一条新线,同时保留之前已经绘制的线?

c - 这两个char数组的值有什么区别?

python - 使用 Azure 托管服务标识 (MSI) 连接 Azure SQL Server 数据库

python - 我之前在脚本中定义的函数出现名称错误

spring - WebSockets JSR 356 Spring 集成@ServerEndpoint

python - Websockets,异步处理用户输入。获取连接关闭错误 1006

python - 在Gtk3中使用python将光标更改为沙漏

python-3.x - 如果小数为 0,Pandas 将 float 转换为 int

python - 如何在 csv 文件中查找元素并编辑该文件

javascript - 服务器发送事件与 websockets?