You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.24 02:23:09