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

跨线程使用Autobahn(Twisted) WebSocket与ROS节点通信时触发AttributeError的问题求助

解决Autobahn Twisted WebSocket与ROS线程通信的AttributeError问题

这个错误的根源很明确:你在调用WebSocket发送消息时,误用了类对象而非已建立连接的协议实例。让我一步步拆解问题并给出修复方案:

错误原因分析

在你的RosNode.send_data方法中,这段代码是问题核心:

reactor.callFromThread(self.factory.protocol.sendMessage, self.factory.protocol, msg.encode('utf8'))

这里的self.factory.protocol指向的是MyServerProtocol这个类,而不是客户端连接后创建的具体实例。当你直接调用类的sendMessage方法时,Python会把类本身作为self参数传入,但state是WebSocket协议实例的属性(只有当客户端连接建立后,实例化的协议对象才会拥有这个状态属性),类本身并没有state,所以就抛出了AttributeError。

修复步骤

1. 在工厂类中维护已连接的客户端实例

首先修改MyServerProtocolFactory,添加一个列表来跟踪所有活跃的WebSocket连接实例:

class MyServerProtocolFactory(WebSocketServerFactory):
    def __init__(self):
        super().__init__()
        # 存储所有已建立连接的协议实例
        self.active_connections = []

    def buildProtocol(self, *args, **kwargs):
        protocol = MyServerProtocol()
        protocol.factory = self
        return protocol

2. 在协议类中更新连接列表

修改MyServerProtocol的onOpen和onClose方法,在连接建立/关闭时更新工厂的连接列表:

class MyServerProtocol(WebSocketServerProtocol):
    def onConnect(self, request):
        print("Client connecting: {}".format(request.peer))

    def onOpen(self):
        print("WebSocket connection open.")
        # 连接建立时,将当前实例加入活跃连接列表
        self.factory.active_connections.append(self)

    def onMessage(self, payload, isBinary):
        try:
            obj = json.loads(payload.decode('utf8'))
            q.put(obj)
            resp = qr.get(timeout=1)
            payload = resp.encode('utf8')
            self.sendMessage(payload, isBinary=False)
        except:
            print("Unable to convert to json")

    def onClose(self, wasClean, code, reason):
        print("WebSocket connection closed: {}".format(reason))
        # 连接关闭时,从列表中移除当前实例
        if self in self.factory.active_connections:
            self.factory.active_connections.remove(self)

3. 修改ROS线程中的发送逻辑

最后,在RosNode.send_data方法中,针对活跃的连接实例调用sendMessage,而不是类:

def send_data(self, msg, name, msg_type):
    if msg_type == "service response":
        qr.put(msg)
    if msg_type == "subscriber":
        # 定义一个在Twisted主线程执行的发送函数
        def broadcast_to_clients():
            # 遍历所有活跃连接,逐个发送消息
            for conn in self.factory.active_connections:
                # 先检查连接状态,避免向已关闭的连接发送
                if conn.state == self.factory.protocol.STATE_OPEN:
                    conn.sendMessage(msg.encode('utf8'), isBinary=False)
        # 用callFromThread将发送逻辑调度到Twisted主线程执行
        reactor.callFromThread(broadcast_to_clients)

额外注意事项

  • 如果你的应用只需要支持单个客户端连接,可以简化逻辑:只保存最后一个连接的实例(比如用self.last_connection代替列表),避免遍历。
  • 务必在发送前检查连接状态,否则可能在连接关闭后发送消息引发新的错误。
  • 确保你的RosNode初始化时正确接收了factory参数,这样self.factory才能正确引用到工厂实例。

内容的提问来源于stack exchange,提问作者ASuitee

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.04.30 03:17:32