Python Tornado websocket客户端丢失响应消息?

Python Tornado websocket客户端丢失响应消息?,python,websocket,tornado,Python,Websocket,Tornado,我需要处理来自网络摄像头的帧,并将一些选定的帧发送到远程websocket服务器。服务器会立即回复一条确认消息(很像echo服务器)。 帧处理速度慢且cpu密集,因此我希望使用单独的线程池(生产者)来使用所有可用的内核。因此,客户机(消费者)在池有东西要发送之前一直处于空闲状态。 我当前的实现(见下文)只有在producer测试循环中添加一个小的sleep时才能正常工作。如果我删除此延迟,我将停止从服务器(echo服务器和我的真实服务器)接收任何应答。连第一个答案都没有了,所以我不认为这是一个防

我需要处理来自网络摄像头的帧,并将一些选定的帧发送到远程websocket服务器。服务器会立即回复一条确认消息(很像echo服务器)。 帧处理速度慢且cpu密集,因此我希望使用单独的线程池(生产者)来使用所有可用的内核。因此,客户机(消费者)在池有东西要发送之前一直处于空闲状态。 我当前的实现(见下文)只有在producer测试循环中添加一个小的sleep时才能正常工作。如果我删除此延迟,我将停止从服务器(echo服务器和我的真实服务器)接收任何应答。连第一个答案都没有了,所以我不认为这是一个防洪机制。 我做错了什么

import tornado
from tornado.websocket import websocket_connect
from tornado import gen, queues

import time

class TornadoClient(object):

    url = None
    onMessageReceived = None
    onMessageSent = None

    ioloop = tornado.ioloop.IOLoop.current()
    q = queues.Queue()

    def __init__(self, url, onMessageReceived, onMessageSent):
        self.url = url
        self.onMessageReceived = onMessageReceived
        self.onMessageSent = onMessageSent

    def enqueueMessage(self, msgData, binary=False):
        print("TornadoClient.enqueueMessage")

        self.ioloop.add_callback(self.addToQueue, (msgData, binary))

        print("TornadoClient.enqueueMessage done")

    @gen.coroutine
    def addToQueue(self, msgTuple):
        yield self.q.put(msgTuple)

    @gen.coroutine
    def main_loop(self):

        connection = None
        try:
            while True:

              while connection is None:
                try:
                    print("Connecting...")
                    connection = yield websocket_connect(self.url)
                    print("Connected " + str(connection))
                except Exception, e:
                    print("Exception on connection " + str(e))
                    connection = None
                    print("Retry in a few seconds...")
                    yield gen.Task(self.ioloop.add_timeout, time.time() + 3)

              try:
                  print("Waiting for data to send...")
                  msgData, binaryVal  = yield self.q.get()
                  print("Writing...")
                  sendFuture = connection.write_message(msgData, binary=binaryVal)
                  print("Write scheduled...")
              finally:
                self.q.task_done()

              yield sendFuture
              self.onMessageSent("Sent ok")

              print("Write done. Reading...")
              msg = yield connection.read_message()
              print("Got msg.")
              self.onMessageReceived(msg)

              if msg is None:
                print("Connection lost")
                connection = None

            print("main loop completed")
        except Exception, e:
            print("ExceptionExceptionException")
            print(e)
            connection = None

        print("Exit main_loop function")

    def start(self):
        self.ioloop.run_sync(self.main_loop)
        print("Main loop completed")


######### TEST METHODS #########

def sendMessages(client):
    time.sleep(2)   #TEST only: wait for client startup
    while True:
        client.enqueueMessage("msgData", binary=False)
        time.sleep(1)  # <--- comment this line to break it

def testPrintMessage(msg):
    print("Received: " + str(msg))

def testPrintSentMessage(msg):
    print("Sent: " + msg)


if __name__=='__main__':

    from threading import Thread

    client = TornadoClient("ws://echo.websocket.org", testPrintMessage, testPrintSentMessage)

    thread = Thread(target = sendMessages, args = (client, ))
    thread.start()

    client.start()

这是一个简洁的代码。我相信在
sendmages
线程中需要睡眠的原因是,否则,它会以每秒数百万次的速度不断调用
enqueueMessage
。由于
enqueueMessage
不会等待处理已排队的消息,因此它会继续调用
IOLoop.add_callback
尽可能快,而不会给循环足够的机会执行回调

循环可能会在主线程上运行一些,因为您实际上并没有阻止它。但是
sendMessages
线程添加回调的速度比循环处理回调的速度快得多。当循环从队列中弹出一条消息并开始处理它时,已经添加了数以百万计的新回调,循环必须先执行这些回调,然后才能进入消息处理的下一阶段


因此,对于您的测试代码,我认为在线程上调用
enqueueMessage
之间休眠是正确的。我相信在
sendmages
线程中需要睡眠的原因是,否则,它会以每秒数百万次的速度不断调用
enqueueMessage
。由于
enqueueMessage
不会等待处理已排队的消息,因此它会继续调用
IOLoop.add_callback
尽可能快,而不会给循环足够的机会执行回调

循环可能会在主线程上运行一些,因为您实际上并没有阻止它。但是
sendMessages
线程添加回调的速度比循环处理回调的速度快得多。当循环从队列中弹出一条消息并开始处理它时,已经添加了数以百万计的新回调,循环必须先执行这些回调,然后才能进入消息处理的下一阶段


因此,对于您的测试代码,我认为在线程上调用
enqueueMessage
之间休眠是正确的。

好的,我看到了。这个最小的例子有点极端:)但为什么我在实际代码中看到了相同的行为?我将在我的问题中添加一些额外的信息。好的,我看到了。这个最小的例子有点极端:)但为什么我在实际代码中看到了相同的行为?我将在我的问题中添加一些额外的信息。
2017-05-13 18:59:54+0200 [-] TX Frame to tcp4:192.168.0.5:48964 : fin = True, rsv = 0, opcode = 1, mask = -, length = 21, repeat_length = None, chopsize = None, sync = False, payload = {"type": "PROCESSED"}
2017-05-13 18:59:54+0200 [-] TX Octets to tcp4:192.168.0.5:48964 : sync = False, octets = 81157b2274797065223a202250524f434553534544227d