我正在为服务器和客户端构建一个简单的应用程序,以便在构建更复杂的应用程序之前提前传递数据。这个问题的目的很简单。在这里,客户端每秒都会向服务器创建数据,如果从客户端收到的数据是"发送"等文本消息,服务器也会发回一些数据。我几乎要创建这个类似的应用程序。但它没有工作,所以我花了差不多一个星期的时间来浪费我的周末。
问题是服务器收到消息后意味着要求向客户端发送消息,服务器似乎按照日志发送了数据,但客户端没有响应。根据我的理解,我认为名为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和其他。但它没有用。
答案 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")