I am building a simple application for both a server and a client to communicate data in advance that I build more complex application. Purpose of this problem is very simple. I
Why is this happening?
It's happening because the while
loop in the run
method is iterating faster than Tornado could call the cb_receive
.
A dirty hack to get around this is to sleep for a small time at the end of the loop. This way, IOLoop
becomes free and can run other coroutines and callbacks.
Example:
while True:
# other code ...
yield gen.sleep(0.01)
If you run your client, you'll see the cb_receive
callback being called whenever the server sends a message.
But this is a very bad solution. I just mentioned it so the actual problem can be apparent. And now, I think you know the reason why the cb_recieve
callback wasn't being called.
What's the solution to this?
The real reason why this is problem is happening is because while
loop is too fast. A dirty solution to that is to put the loop to sleep for a little time.
But that's a very inefficient solution. Because input()
function is blocking in nature. So, when the while
loop reaches at msg = input()
line, the whole IOLoop just hangs there. This means Tornado cannot run anything else until you input a message. If the server sends more messages during that time, Tornado won't be able to run the callback.
Normally, a non-blocking application should be able to do other things while it is waiting for something or some event. For example, if Tornado is waiting for your input, it should be able to run other things while you haven't provided it any input. But that's not the case because input()
function is blocking.
A better solution would be to take user input in a non-blocking manner. You can use sys.stdin
for this task.
Example (modified code from 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")