跨线程使用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
相关产品推荐
相关产品推荐

