Tornado 客户端 on_message_callback 没有响应

tornado client on_message_callback is not responding

我正在为服务器和客户端构建一个简单的应用程序,以便在我构建更复杂的应用程序之前提前传递数据。这个问题的目的很简单。在这里,客户端每秒向服务器创建数据,如果从客户端接收到的数据是文本消息,如 "send",服务器也会发回一些数据。我几乎要创建这个类似的应用程序。但是它没有用,所以我花了将近一周的时间来浪费我的周末。

问题是服务器收到请求客户端消息的消息后,服务器好像按照日志发送了数据,但是客户端没有响应。据我了解,我认为调用 cb_receive() 的回调函数应该对此做出响应。

我用下面的简单应用程序创建了这个问题。如果您擅长 asyncio 和 tornado 库,请告诉我。再次感谢!

服务器端

import tornado.ioloop
import tornado.web
import tornado.websocket
import os
from tornado import gen

class EchoWebSocket(tornado.websocket.WebSocketHandler):
    def open(self):
        self.write_message('hello')

    @gen.coroutine
    def on_message(self, message):
        print(message)
        yield self.write_message('notification : ', message)

    def on_close(self):
        print("A client disconnected!!")

if __name__ == "__main__":
    app = tornado.web.Application([(r"/", EchoWebSocket)])
    app.listen(os.getenv('PORT', 8344))
    tornado.ioloop.IOLoop.instance().start()

客户端

from tornado.ioloop import IOLoop, PeriodicCallback
from tornado import gen
from tornado.websocket import websocket_connect



@gen.coroutine
def cb_receive(msg):
    print('msg----------> {}'.format(msg))

class Client(object):
    def __init__(self, url, timeout):
        self.url = url
        self.timeout = timeout
        self.ioloop = IOLoop.instance()
        self.ws = None
        self.connect()
        self.ioloop.start()

    @gen.coroutine
    def connect(self):
        print("trying to connect")
        try:
            self.ws = yield websocket_connect(self.url,on_message_callback=cb_receive)
        except Exception as e:
            print("connection error")
        else:
            print("connected")
            self.run()

    @gen.coroutine
    def run(self):
        while True:
            print('please input')
            msg = input()
            yield self.ws.write_message(msg)
            print('trying to send msg {}'.format(msg))

if __name__ == "__main__":
    client = Client("ws://localhost:8344", 5)

请帮帮我!我不仅尝试了上面的这个龙卷风库,还尝试了 websockets 和其他库。但是没用。

为什么会这样?

这是因为 run 方法中的 while 循环比 Tornado 调用 cb_receive.

方法迭代得更快

解决这个问题的一个肮脏的技巧是在循环结束时休眠一小段时间。这样,IOLoop 变得自由,并且可以 运行 其他协程和回调。

示例:

while True:
    # other code ...
    yield gen.sleep(0.01)

如果您 运行 您的客户端,您会看到 cb_receive 回调在服务器发送消息时被调用。

但这是一个非常糟糕的解决方案。我刚刚提到它,所以实际问题很明显。现在,我想您知道 cb_recieve 回调未被调用的原因了。


有什么解决办法?

出现此问题的真正原因是 while 循环太快。一个肮脏的解决方案是让循环休眠一段时间。

但这是一个非常低效的解决方案。因为 input() 函数本质上是阻塞的。因此,当 while 循环到达 msg = input() 行时,整个 IOLoop 就挂在那里。这意味着在您输入消息之前,Tornado 不能 运行 任何其他内容。如果服务器在此期间发送更多消息,Tornado 将无法 运行 回调。

通常情况下,非阻塞应用程序在等待某事或某个事件时应该能够做其他事情。例如,如果 Tornado 正在等待您的输入,它应该能够 运行 在您没有提供任何输入的情况下进行其他操作。但事实并非如此,因为 input() 函数正在阻塞。

更好的解决方案是以非阻塞方式接收用户输入。您可以使用 sys.stdin 完成此任务。

示例(修改自 的代码):

import sys

class Client:
    ...
    self.ioloop.add_handler(sys.stdin, self.handle_input, IOLoop.READ)


    @gen.coroutine
    def handle_input(self, fd, events):
        msg = fd.readline()
        yield self.ws.write_message(msg)

    @gen.coroutine
    def run(self):
        # the run method would have nothing
        # but you can put a print statement
        # here, remove everything else

        print("please input")



# a small optional modification to the cb_recieve function
@gen.coroutine
def cb_receive(msg):
    print('msg----------> {}'.format(msg))

    # the following print statement is just there
    # to mimic the while loop behaviour
    # to print a "please input" message 
    # to ask user for input because 
    # sys.stdin in always listening for input

    print("please input")