简体   繁体   English

如何使用socketio异步发送数据到Web客户端?

[英]How to asynchronously send data with socketio to a web client?

The following situation: 以下情况:

  • Web client: Using JavaScript socketio to listen for incoming messages (= JavaScript). Web客户端:使用JavaScript socketio侦听传入的消息(= JavaScript)。
  • Web server: Using flask-socketio with eventlet to send data (= Python). Web服务器:使用flask-socketioeventlet发送数据(= Python)。

Everything works if the client sends a message to the server. 如果客户端向服务器发送消息,则一切正常。 The server receives the messages. 服务器接收消息。 Example: 例:

socketio = SocketIO(app, engineio_logger=True, async_mode="eventlet")

@socketio.on("mymsg")
def handle_event(message):
    print("received message: " + str(message))

Unfortunately the other way around does not work - to some extent. 不幸的是,反过来也行不通 - 在某种程度上。 I have a thread producing live data about 5 to 10 times a second the web frontend should display. 我有一个线程产生实时数据,每秒约5到10次Web前端应该显示。 It should be sent to the client. 它应该发送给客户端。

First: It does not work at all if the thread producing the data tries to invoke sockeito.emit() directly. 第一:如果生成数据的线程试图直接调用sockeito.emit()它根本不起作用。 The reason for that is unclear to me but somehow plausible as flask-socketio with eventlet follows different async models, as the documentation says. 其中的原因尚不清楚,但有些方面似乎有道理,因为带有eventlet flask-socketio eventlet遵循不同的异步模型,正如文档所述。

Second: Decoupling classic threads from the async model of flask/eventlet works to some extent. 第二:将经典线程与flask / eventlet的异步模型分离在一定程度上起作用。 I attempt to use an eventlet queue for that. 我尝试使用eventlet队列。 All status data my thread produces is put into the queue like this: 我的线程产生的所有状态数据都放入队列中,如下所示:

statusQueue.put(statusMsg)

This works fine. 这很好用。 Debugging messages show that this is performed all the time, adding data after data to the queue. 调试消息显示此操作始终执行,将数据添加到队列之后。

As the documentation of flasks tells I'm adviced to use socketio.start_background_task() in order to get a running "thread" in a compatible mode to the async model socketio uses. 正如烧瓶的文档告诉我,我建议使用socketio.start_background_task()以便在兼容模式下获得正在运行的“线程” socketio async async model。 So I am using this code: 所以我使用这段代码:

def emitStatus():
    print("Beginning to emit ...")
    while True:
        msg = statusQueue.get()
        print("Sending status packet: " + str(msg))
        socketio.emit("status", msg, broadcast=True)
        statusQueue.task_done()
        print("Sending status packet done.")
print("Terminated.")

socketio.start_background_task(emitStatus)

The strange thing where I'm asking you for help is this: The first call to statusQueue.get() blocks as expected as initially the queue is empty. 我要求你帮忙的奇怪之处是:第一次调用statusQueue.get()阻塞,因为最初队列是空的。 The first message is taken from the queue and sent via socketio . 第一条消息从队列中获取并通过socketio发送。 Debug messages at the client show that the web client receives this message. 客户端上的调试消息显示Web客户端收到此消息。 Debug messages at the server show that the message is sent successfully. 服务器上的调试消息显示消息已成功发送。 But: As soon as the next statusQueue.get() is invoked, the call blocks indefinitely, regardless of how many messages get put into the queue. 但是:只要调用下一个statusQueue.get() ,调用就会无限期地阻塞,无论有多少消息被放入队列。

I'm not sure if this helps but some additional information: The socketio communication is perfectly intact. 我不确定这是否有帮助,但是还有一些额外的信息: socketio通信完好无损。 If the client sends data, everything works. 如果客户端发送数据,一切正常。 Additionally I can see the ping-pongs both client and server play to keep the connections alive. 此外,我可以看到客户端和服务器的ping-pongs一直保持连接活动。

My question is: How can I properly implement a server that is capable of sending messages to the client asynchronously? 我的问题是:如何正确实现能够异步向客户端发送消息的服务器?

Have a look at https://github.com/jkpubsrc/experiment-python-flask-socketio for a minimalistic code example featuring the Python-Flask server process and a JQuery based JavaScript client. 有关Python-Flask服务器进程和基于JQuery的JavaScript客户端的简约代码示例, 查看https://github.com/jkpubsrc/experiment-python-flask-socketio

(FYI: As these are status messages not necessarily every message needs to arrive. But I very much would like to receive at least some messages not only the very first message and then no other message.) (仅供参考:由于这些是状态消息,因此不一定每条消息都需要到达。但我非常希望至少收到一些消息,不仅是第一条消息,还有其他消息。)

Thank you for your responses. 谢谢你的回复。

I left two solutions to make the code work as pull requests. 我留下了两个解决方案,使代码可以作为拉取请求。

Basically, the answer is: you choose one technology and stick a process with it: 基本上,答案是:您选择一种技术并坚持使用它:

  • Going async_mode=threading ? async_mode=threading Great, use stdlib Queue. 太棒了,使用stdlib队列。 Don't import eventlet unless you have to. 除非必须,否则不要导入eventlet。
  • Going async_mode=eventlet ? async_mode=eventlet Also great, use eventlet Queue and don't forget that stdlib time.sleep or socket IO will block everything else, fix with eventlet.monkey_patch() 也很大,使用eventlet队列和不要忘记,STDLIB time.sleepsocket IO会阻止其他的一切,与固定eventlet.monkey_patch()
  • If you must use both eventlet and threading, the best approach is to let them live in separate OS processes and communicate via local socket. 如果必须同时使用eventlet和线程,最好的方法是让它们位于不同的OS进程中并通过本地套接字进行通信。 It's extra work, but it is very robust and you know how it works and why it will not break. 这是额外的工作,但它非常强大,你知道它是如何工作的以及为什么它不会破坏。

With good knowledge of both eventlet and native threads you can carefully mix them into working code. 熟悉eventlet和本机线程,您可以仔细地将它们混合到工作代码中。 As of 2018-09, mixing doesn't work in friendly obvious way, as you already found. 正如您已经发现的那样,截至2018-09,混合不能以友好明显的方式起作用。 Sorry. 抱歉。 Patches are welcome. 欢迎补丁。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM