龙卷风客户端on_message_callback没有响应

时间:2018-04-17 06:12:24

标签: websocket tornado

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

问题是服务器收到消息后意味着要求向客户端发送消息,服务器似乎按照日志发送了数据,但客户端没有响应。根据我的理解,我认为名为cb_receive()的回调函数应该对此做出响应。

我在下面用简单的应用程序创建了这个问题。如果你擅长asyncio和龙卷风图书馆,请告诉我。再次感谢!

服务器端

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和其他。但它没有用。

1 个答案:

答案 0 :(得分:1)

为什么会这样?

这种情况正在发生,因为while方法中的run循环迭代速度比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执行此任务。

示例(来自this answer的修改代码):

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")