【问题标题】:Python Tornado - Message all clientsPython Tornado - 向所有客户发送消息
【发布时间】:2020-12-14 23:41:50
【问题描述】:

我使用 Tornado 作为 Raspberry Pi 上的 websocket 服务器,它接收传入的消息,解析它们,然后有时会将消息广播回所有客户端。这一切都很好,但我希望能够在用户使用 RPi.GPIO 按下按钮时向所有客户端广播消息。这是我目前拥有的代码:

# Define websockets server
define('port', default=8080)

# Global websocket connections
ws_connections = []

# Called from RPi.GPIO Button event detection
def handle_button(pin):
   if pin == 10:
       broadcast_msg = {"request": "broadcast", "data": "test_message"}
       game_controller_broadcast(broadcast_msg)

# Websockets handler
class game_websockets_handler(tornado.websocket.WebSocketHandler):
   
   # Called when new connection opened
   def open(self):
       ws_connections.append(self)

   # Called when a message is received
   def on_message(self, message):
       print("Message received: {}".format(message))
       json_data = json.loads(message)
       self.write_message(json.dumps(game_controller_parse_request(json_data)))
   
   # Called when a connection closes
   def on_close(self):
       ws_connections.remove(self)

   # Accept all cross-origin traffic
   def check_origin(self, origin):
       return True

   @classmethod
   def route_urls(cls):
       return [(r'/',cls, {}),]

def game_websockets_init():
   #create a tornado application and provide the urls
   app = tornado.web.Application(game_websockets_handler.route_urls())

   #setup the server
   server = tornado.httpserver.HTTPServer(app)
   server.listen(options.port)
   
   #start io/event loop
   tornado.ioloop.IOLoop.instance().start()

def game_controller_broadcast(json_message):
   [client.write_message(json.dumps(json_message)) for client in ws_connections]

如果我从game_controller_parse_request() 中调用game_controller_broadcast(),它就可以正常工作。但是当我从handle_button() 中调用它时,我得到了错误:

RuntimeError:线程“Dummy-1”中没有当前事件循环。

我在这里缺少什么?如何从 Tornado 外部广播消息?

谢谢。

【问题讨论】:

    标签: python websocket raspberry-pi tornado


    【解决方案1】:

    看起来handle_button() 的任何调用都是在单独的线程上执行的。 Tornado 中唯一的线程安全方法是IOLoop.add_callback,所以你需要这样做才能将控制权转移到 IOLoop 线程:

    def handle_button(pin):
        if pin == 10:
            broadcast_msg = {"request": "broadcast", "data": "test_message"}
            main_io_loop.add_callback(lambda:
                game_controller_broadcast(broadcast_msg))
        
    

    在启动 IOLoop 或继续调用 handle_button 的线程之前,您必须将 main_io_loop 保存在主线程的全局变量中。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2019-04-29
      • 1970-01-01
      • 1970-01-01
      • 2016-05-25
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多