Python3 Websocket Server阻塞问题:如何同时运行服务与数据采集?
问题解决:Websocket服务与数据采集并行运行
你的核心问题出在线程创建时的错误调用,以及一些代码逻辑问题,以下是具体修复方案:
1. 线程启动的致命错误
原代码中thread = Thread(target=server.run_forever(),daemon=True)这一行,你直接执行了server.run_forever()并把返回值传给线程target,导致主线程直接阻塞在这个方法上,根本没启动新线程。正确的做法是传递方法本身(不带括号),然后调用thread.start()启动线程。
2. 其他需要修复的问题
semaphore未定义:run_command方法里使用了with semaphore但没有初始化,需要添加信号量定义并初始化。run_command重复执行命令:同时调用subprocess.Popen(cmd)和os.system(cmd)会执行两次命令,选其一即可。setupI2c退出码判断错误:os.system返回的是整数类型的退出码,和字符串"0"比较永远不成立,应该直接和整数0比较。- 缺少Websocket回调:要实现网页控制和状态推送,需要给服务器添加客户端连接、消息接收的回调函数。
修正后的完整代码
#!/usr/bin/python3 import sys sys.path.append("/home/willi/shared/lib") from threading import Thread, Semaphore from websocket_server import WebsocketServer import os import time import subprocess from compass.compass import compass from webserv.wifiscan import wifiSigLevel from accelerometer.inclin import inclin PORT_NUMBER = 8080 class potatoSetup(): def __init__(self): self.semaphore = Semaphore(1) # 初始化信号量 self.server = None self.compass = None self.pitchRoll = None self.wsl = None def run_command(self, cmd): with self.semaphore: # 用subprocess执行命令,可捕获输出和错误 result = subprocess.run(cmd, shell=True, capture_output=True, text=True) print(f"Command '{cmd}' executed, exit code: {result.returncode}") if result.stdout: print(f"Output: {result.stdout.strip()}") if result.stderr: print(f"Error: {result.stderr.strip()}") def setupI2c(self): # os.system返回整数退出码,0表示执行成功 i2c_result = os.system("i2cdetect -y 1") if i2c_result != 0 : os.system("sudo ldto enable i2c-b") # Websocket回调:客户端连接时触发 def new_client(self, client, server): print(f"New client connected: {client['address']}") # 给新客户端发送初始状态 if self.compass and self.pitchRoll: heading = self.compass.getDirection() angle_xz, angle_yz = self.pitchRoll.get_inclination() status_msg = f"Heading: {heading}, Pitch: {angle_xz:.2f}deg, Roll: {angle_yz:.2f}deg" server.send_message(client, status_msg) # Websocket回调:接收客户端消息时触发(处理网页控制命令) def message_received(self, client, server, message): print(f"Message from {client['address']}: {message}") # 根据消息执行对应操作 if message == "GET_STATUS": heading = self.compass.getDirection() angle_xz, angle_yz = self.pitchRoll.get_inclination() status_msg = f"Heading: {heading}, Pitch: {angle_xz:.2f}deg, Roll: {angle_yz:.2f}deg" server.send_message(client, status_msg) elif message.startswith("RUN_CMD:"): cmd = message.split(":", 1)[1] self.run_command(cmd) def runServer(self): PORT = 9001 HOST = '192.168.1.245' try: self.server = WebsocketServer(PORT, HOST) # 设置回调函数 self.server.set_fn_new_client(self.new_client) self.server.set_fn_message_received(self.message_received) # 创建线程并启动,target传方法名(不带括号) thread = Thread(target=self.server.run_forever, daemon=True) thread.start() print(f"Websocket server started on {HOST}:{PORT}") except Exception as err : print(f"Server start failed: {err}") if __name__ == "__main__": tade = potatoSetup() tade.setupI2c() # 初始化硬件对象,存入类实例方便回调使用 tade.compass = compass() tade.pitchRoll = inclin(0x68) tade.wsl = wifiSigLevel("webserv/") # 启动Websocket服务(非阻塞) tade.runServer() # 初始状态输出 angle_xz, angle_yz = tade.pitchRoll.get_inclination() print(f"XZ angle = {angle_xz:6.2f}deg YZ angle = {angle_yz:6.2f}deg") # 数据采集主循环 while True: heading = tade.compass.getDirection() angle_xz, angle_yz = tade.pitchRoll.get_inclination() # 更新控制台输出 sys.stdout.write(f"\rHeading: {heading} Pitch = {angle_xz:6.2f}deg Roll = {angle_yz:6.2f}deg") sys.stdout.flush() # 定期向所有客户端推送状态 if tade.server: status_msg = f"Heading: {heading}, Pitch: {angle_xz:.2f}deg, Roll: {angle_yz:.2f}deg" tade.server.send_message_to_all(status_msg) time.sleep(0.5)
关键说明
- 线程启动改为
Thread(target=self.server.run_forever, daemon=True)后,Websocket服务会在后台线程运行,主线程可以继续执行数据采集循环。 - 添加了Websocket回调函数,实现了客户端连接通知、消息处理(比如网页发送
GET_STATUS获取状态,RUN_CMD:xxx执行命令),以及定期向所有客户端推送状态的功能。 - 修复了信号量、命令执行、I2C判断等逻辑错误,代码更健壮。
内容的提问来源于stack exchange,提问作者cyberbilly
相关产品推荐
相关产品推荐

