我正在使用 Python 创建一个桌面应用程序,并使用 Vue 3 编写 GUI,并且我需要在单独的线程中运行 WebSocket 服务器。问题是 WebSocket 服务器必须持续侦听传入连接以实现与客户端的实时通信,并且我不确定如何在新线程中运行它,这是必要的,因为否则 GUI 将无法启动。有谁知道应该如何实施?因为我不知道该怎么做。
主要代码:
import asyncio
import webview
import threading
from services.HttpServer import HttpServerService
from services.WebSocket import WebSocketService
def on_loaded(window_ready):
webview.windows[0].events.loaded -= on_loaded
print('Val', window_ready);
if (window_ready):
webview.windows[0].set_window_size(width=1200, height=800)
class App:
def __init__(self):
self.http_server_thread = None
self.websocket_thread = None
self.client_ui_thread = None
self.websocket = None
self.client_ui = None
self.webview = webview
async def start(self):
self.http_server_thread = threading.Thread(target=HttpServerService("127.0.0.1", 8001).start)
self.http_server_thread.daemon = True # Set the thread as daemon
self.http_server_thread.start()
self.websocket = WebSocketService("127.0.0.1", 8008)
self.websocket_thread = threading.Thread(target=await self.websocket.start)
self.websocket_thread.daemon = True # Set the thread as daemon
self.websocket_thread.start()
self.client_ui = webview.create_window("CrowdQuant", url="http://127.0.0.1:8001", width=1024, height=770,
resizable=False, frameless=True)
self.client_ui.events.loaded += lambda: self.websocket.on_message(variable_name="window_ready",
handle_message_fn=lambda
window_ready: on_loaded(window_ready))
self.client_ui_thread = threading.Thread(target=webview.start) # gui="cef"
self.client_ui_thread.daemon = True # Set the thread as daemon
self.client_ui_thread.start()
if __name__ == "__main__":
asyncio.run(App().start())
WebSocket服务代码:
import json
import asyncio
import websockets
class WebSocketService:
def __init__(self, address="127.0.0.0", port=8008):
self.socket = None
self.url = (address, port)
self.variable_name = None
self.handle_message_fn = None
self.server = None
self.clients = set()
async def start(self):
await self.start_server()
await self.start_client()
async def start_server(self):
while True:
try:
self.server = await websockets.serve(self.handle_client_connection, self.url[0], self.url[1])
print(f"WebSocket server started at ws://{self.url[0]}:{self.url[1]}")
await self.server.wait_closed()
except OSError:
print(f"Address {self.url[0]}:{self.url[1]} already in use. Retrying...")
await asyncio.sleep(5)
async def handle_client_connection(self, websocket):
self.clients.add(websocket)
try:
async for message in websocket:
await self.handle_message(message)
finally:
self.clients.remove(websocket)
async def stop(self):
await self.stop_client()
await self.stop_server()
async def stop_server(self):
if self.server:
self.server.close()
await self.server.wait_closed()
@staticmethod
def on_error(error):
print('WebSocket error:', error)
您的实施似乎总体上是在正确的轨道上,但有一些问题需要解决。让我们一步一步地看一下它们。
线程中的异步:您正在尝试将异步与线程一起使用,这并不简单。尽管可以在线程中运行 asyncio,但由于其复杂性和潜在的问题,通常不建议这样做。
启动 WebSocket 服务:由于 asyncio 不容易与设置中的线程集成,因此您应该考虑直接在 asyncio 事件循环中运行 WebSocket 服务器,而不是尝试在单独的线程中运行它。
以下是如何修改代码以将 WebSocket 服务器集成到 asyncio 事件循环中:
import asyncio
import webview
import threading
from services.HttpServer import HttpServerService
from services.WebSocket import WebSocketService
def on_loaded(window_ready):
webview.windows[0].events.loaded -= on_loaded
print('Val', window_ready);
if (window_ready):
webview.windows[0].set_window_size(width=1200, height=800)
class App:
def __init__(self):
self.http_server_thread = None
self.client_ui_thread = None
self.websocket = None
self.client_ui = None
self.webview = webview
async def start(self):
self.http_server_thread = threading.Thread(target=HttpServerService("127.0.0.1", 8001).start)
self.http_server_thread.daemon = True
self.http_server_thread.start()
self.websocket = WebSocketService("127.0.0.1", 8008)
await self.websocket.start_server() # Start WebSocket server directly
self.client_ui = webview.create_window("CrowdQuant", url="http://127.0.0.1:8001", width=1024, height=770,
resizable=False, frameless=True)
self.client_ui.events.loaded += lambda: self.websocket.on_message(variable_name="window_ready",
handle_message_fn=lambda
window_ready: on_loaded(window_ready))
self.client_ui_thread = threading.Thread(target=webview.start)
self.client_ui_thread.daemon = True
self.client_ui_thread.start()
if __name__ == "__main__":
asyncio.run(App().start())
对于 WebSocketService,您可以保持代码不变,因为它已经正确使用了 asyncio。
通过进行这些更改,您应该让 WebSocket 服务器与 GUI 应用程序一起运行,而不需要线程。