[英]Python Tornado: Sending websocket messages from another class
[英]Python Tornado send WebSocket messages from another thread
我想在 Python 中使用 WebSocket,以使 web 客戶端了解我使用 PySerial 從串行端口讀取的數據的最新信息。 我目前正在使用以下代碼通過單獨的線程連續讀取串行數據
def read_from_port():
while running:
reading = ser.readline().decode()
handle_data(reading)
thread = threading.Thread(target=read_from_port)
thread.daemon = True
thread.start()
我正在對串行數據執行一些處理,然后如果計算結果與其先前的值不同,我想向所有連接的 WebSocket 客戶端廣播一條消息。 為此,我設置了以下代碼
clients = []
def Broadcast(message):
for client in clients:
client.sendMessage(json.dumps(message).encode('utf8'))
print("broadcasted")
worker.broadcast = Broadcast
class WSHandler(tornado.websocket.WebSocketHandler):
def open(self):
print('new connection')
clients.append(self)
def on_message(self, message):
print('message received: %s' % message)
response = handler.HandleRequest(message, self.write_message)
def on_close(self):
print('connection closed')
clients.remove(self)
def check_origin(self, origin):
return True
application = tornado.web.Application([
(r'/ws', WSHandler),
])
if __name__ == "__main__":
http_server = tornado.httpserver.HTTPServer(application)
http_server.listen(8765)
myIP = socket.gethostbyname(socket.gethostname())
print('*** Websocket Server Started at %s***' % myIP)
tornado.ioloop.IOLoop.instance().start()
然后我想在工作人員中使用“廣播”方法來廣播結果。 在工作線程中使用此方法會產生以下錯誤
File "main.py", line 18, in Broadcast
client.write_message(message)
File "/usr/local/lib/python3.8/site-packages/tornado/websocket.py", line 342, in write_message
return self.ws_connection.write_message(message, binary=binary)
File "/usr/local/lib/python3.8/site-packages/tornado/websocket.py", line 1098, in write_message
fut = self._write_frame(True, opcode, message, flags=flags)
File "/usr/local/lib/python3.8/site-packages/tornado/websocket.py", line 1075, in _write_frame
return self.stream.write(frame)
File "/usr/local/lib/python3.8/site-packages/tornado/iostream.py", line 555, in write
future = Future() # type: Future[None]
File "/usr/local/Cellar/python@3.8/3.8.3_1/Frameworks/Python.framework/Versions/3.8/lib/python3.8/asyncio/events.py", line 639, in get_event_loop
raise RuntimeError('There is no current event loop in thread %r.'
RuntimeError: There is no current event loop in thread 'Thread-1'.
我了解問題是 Tornado write_message function 不是線程安全的,並且正在產生此錯誤,因為我試圖直接從工作線程調用 function。 據我所知,在 Tornado 中使用並發代碼的推薦方法是通過 asyncio,但我認為在這種情況下線程方法可能更合適,因為我有一個基本上持續並行運行的循環。
不幸的是,我對 asyncio 以及如何在 Python 中實現線程知之甚少,所以我想找出從不同線程發送 WebSocket 消息的最簡單方法是什么。
在https://docs.python.org/3/library/asyncio-dev.html#asyncio-multithreading閱讀有關同時使用 asyncio 和多線程的官方文檔給了我必要的線索,即使用“call_soon_threadsafe”可以非常優雅地實現這一點" function。 因此,以下代碼似乎可以解決問題
tornado.ioloop.IOLoop.configure("tornado.platform.asyncio.AsyncIOLoop")
io_loop = tornado.ioloop.IOLoop.current()
asyncio.set_event_loop(io_loop.asyncio_loop)
clients = []
def bcint(message):
for client in clients:
client.write_message(message)
print("broadcasted")
def Broadcast(message):
io_loop.asyncio_loop.call_soon_threadsafe(bcint, message)
worker.broadcast = Broadcast
class WSHandler(tornado.websocket.WebSocketHandler):
def open(self):
print('new connection')
clients.append(self)
def on_message(self, message):
print('message received: %s' % message)
response = handler.HandleRequest(message, self.write_message)
def on_close(self):
print('connection closed')
clients.remove(self)
def check_origin(self, origin):
return True
application = tornado.web.Application([
(r'/ws', WSHandler),
])
if __name__ == "__main__":
http_server = tornado.httpserver.HTTPServer(application)
http_server.listen(8765)
myIP = socket.gethostbyname(socket.gethostname())
print('*** Websocket Server Started at %s***' % myIP)
tornado.ioloop.IOLoop.current().start()
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.