完全解决AS3 中使用Socket的安全问题.

本文详细记录了解决AS3中使用Socket的安全问题的过程,包括错误信息、尝试的解决方案以及最终的成功策略。作者通过调整服务器端代码,正确处理843端口的安全策略文件请求,最终实现了稳定可靠的Socket通信。

 

通过这篇文章,我完全解决了一个AS3 中使用Socket的安全问题.

按照这篇文章这样子做,不用看其他的文章,就解决了,而且很简单.

讲一下我的目标和遇到的问题以及解决的路径:

1、首先这是一个网站,通过点击一个按钮,然后发送命令到终端;(类似点对点的聊天)

2、然后我就做了一个类似私聊的功能,把这个功能的代码完全复制到我的项目里面的时候,就出现了这个错误【错误信息: Error #2044: 未处理的 SecurityErrorEvent:。 text=Error #2048: 安全沙箱冲突:http://web.a.com/online.swf 不能从 10.20.199.182:8000 加载数据】,于是就到网上找解决方案:

3、找到的解决方案都说有三种方式:843端口监听、在客户端代码中使用Security.loadPolicyFile("xmlsocket://22.20.192.149:1038");方法、还有一个我不记得了。然后我就试了第二种方法,就是在服务器端放crossdomain.xml文件(这个我在上一篇博客有写怎么放),试了好久,在昨天终于不报上面的SecurityErrorEvent这个错了,虽然不报错了,但是都连接不上,于是又去找资料;

4、实在没办法,有Debug一下,发现报了一个新的错【[SWF] /BrainpowerBlazeds/bin/BrainpowerBlazeds.swf - 1,309,421 bytes after decompression
警告: 等待 socket 策略文件时在 xmlsocket://10.20.192.149:843 上超时(3 秒钟)。这不会造成任何问题,但可访问 http://www.adobe.com/go/strict_policy_files_cn 以获得说明。】

5、根据这个错,才找到以下的这篇我转载过来的非常好的资料,才完全解决了。

总的来说:服务器端(java端)的主调程序我新建了两个ServerSocket:A和B。我用一个A监听843端口,如果发来<policy-file-request/>,我就调用一个线程Server843Thread(自定义的类)把策略文件(String)发送回去,接着把这个A关掉。这时候B还是打开的,所以当客户端发来<doopen-file-request/>(这个是客户端发来的我自定义的一个字符串),服务器判断发来的是<doopen-file-request/>而不是<policy-file-request/>,就调用另外一个线程ChatThread(专门处理发送过来的我自定义的信息的线程),就ok了。

注意:客户端连接服务端的时候不用管843端口,这是Adobe设置专门用来发送和接受安全策略文件的端口(据说Adobe准备申请该端口为专属端口),只要连接上面说的ServerSocket B中指定的端口即可(也就是除了843的另外一个,这里用的是9999端口),废话好多(希望能提供多一些说明让大家看懂),其实客户端连服务端的代码就是这样:socket.connect("localhost",9999);

说明一下:这是通过监听843端口发送安全策略文件(crossdomain.xml),用自定义端口9999进行自定义内容通信的例子。

 

本文转自:http://hi.baidu.com/fsnhf/blog/item/410c5a456e914f3787947356.html

解决AS3 Socket编程中最令人头疼的问题

--解决AS3 Socket安全问题

什么是最令人头疼的问题?也许大家会异口同声的说:“安全问题”,不错,不仅仅是AS3 的Socket,整个AS3语言中最令人头疼的问题也无非就是安全问题了。

很 多同行的兄弟在郁闷的时候就会骂Adobe。但是,骂归骂,问题终归是要解决的,Adobe做这样的限制肯定是有他的用意的,大家都知道,swf文件是很 容易被反编的,那么也就是说你的swf文件内部与服务器通信的方式及路径是很容易被别人发现的,如果你的服务器中没有任何访问限制,那么你的服务器很容易 被一些人攻破,这并不需要很高的水平,只要一直刷,你的服务器就完了。

兄弟,您骂够了吗?我们来解决问题吧?

用Java写完Socket服务器后,运行,一切正常,用Flex写全Socket客户端后,运行,一切正常,可是当把生成的swf文件拷到其它地方来运行就出错了,总是无法连接服务器,然后就开始抛securityError,下面我们看一下输出信息。

打 开Flash CS3,打开远程调试器,选择菜单如 Debug->Begin Remote Debug Session->ActionScript3.0(中文版选择菜单如:调试->开始 远程调试会话->ActionScript3.0),下面我们打开客户端swf文件(记得此文件一定是调试版swf),则它会自动连接Flash CS3 的调试器,在Flash CS3中输出相关的调试信息。

可以看到输出信息如下:

------------------------------------

[SWF] C:/Users/Administrator/Desktop/MyClientFlash.swf - 1112717 bytes after decompression

警告: [strict] 将忽略 xmlsocket://localhost:9999 处的策略文件,因为出现语法错误。请访问 http://www.adobe.com/go/strict_policy_files_cn 以解决此问题。

 

 

*** 安全沙箱冲突 ***

到 localhost:9999 的连接已停止 - 不允许从 file:///C|/Users/Administrator/Desktop/MyClientFlash.swf 进行连接

错误: 拒绝请求位于 xmlsocket://localhost:9999 的资源(请求者从 file:///C|/Users/Administrator/Desktop/MyClientFlash.swf 发出请求),原因是缺乏策略文件权限。

------------------------------------

原因是没有在服务器的9999端口放置安全策略文件(我写的服务器用的是9999端口),那么好吧,我就在此处给客户端返回了一个安全策略文件信息,此文件格式如下:

<?xml version="1.0"?>

<cross-domain-policy>

    <site-control permitted-cross-domain-policies="all"/>

    <allow-access-from domain="localhost" to-ports="9999,300-400" />

</cross-domain-policy>

上述示例中是允许来自localhost域的swf文件访问9999端口和300至400端口,你也可以用*来代替localhost以允许来自任何域的swf文件访问。

此时我将客户端文件请求的信息在Java中打印出来,看到的是一段包含<policy-file-request/>的字符串,当Java服务器接收到这个字符串时,立即返回安全策略文件字符串。

我想这样应该没什么问题了吧,可是当我再连接服务器时仍然无法连接,输出信息成了这样:

-------------------------------------

[SWF] C:/Users/Administrator/Desktop/MyClientFlash.swf - 1112717 bytes after decompression

警告: 等待 socket 策略文件时在 xmlsocket://localhost:9999 上超时(3 秒钟)。这不会造成任何问题,但可访问 http://www.adobe.com/go/strict_policy_files_cn 以获得说明。

 

*** 安全沙箱冲突 ***

到 localhost:9999 的连接已停止 - 不允许从 file:///C|/Users/Administrator/Desktop/MyClientFlash.swf 进行连接

错误: 拒绝请求位于 xmlsocket://localhost:9999 的资源(请求者从 file:///C|/Users/Administrator/Desktop/MyClientFlash.swf 发出请求),原因是缺乏策略文件权限。

-------------------------------------

这时我并不灰心,就按照它的说明去了http://www.adobe.com/go/strict_policy_files_cn这里查看说明了。

等我看到这个页面时,我真的郁闷了,上面全是乱说一通,根本不着边,全是一些没用的信息,亏Adobe想得出来提示我到这里看。

就 在我将要放弃解决这个问题的时候,我发现了另外一个现象,那就是当我刚开始连接服务器时,服务器端并没有打印出来客户端的请求信息 (<policy-file-request/>),而是在Flash CS3的调试器输出了超时错误之后,服务器端才打印出来这个请求信 息。

这时我看到了一线希望,那就是服务器端确实出现了等待,这肯定是服务器端的程序问题。服务器端接收请求的处理代码片断如下:

BufferedReader br=new BufferedReader(new InputStreamReader(s.getInputStream()));

if(br.readLine().indexOf("<policy-file-request/>")!=-1){

//开始返回授权文件信息

...

}

其中变量s是ServerSocket实例通过accept方法获得的Socket实例。

此时我开始怀疑是readLine方法的问题了,因为readLine方法是当程序读到/n或者/r时才会返回信息,所以肯定是此方法中出现了等待,因为起初客户端并没有传来换行符或者回车符。

于是我改变了读取字符串的方法,不再用readLine了,而取流中前22个字符,代码片断如下所示:

BufferedReader br=new BufferedReader(new InputStreamReader(s.getInputStream()));

char[] ch=new char[22];

br.read(ch, 0, ch.length);

StringBuffer sb=new StringBuffer();

for(int i=0;i< ch.length;i++){

sb.append(ch[i]);

}

String st=sb.toString();

if(st.indexOf("<policy-file-request/>")!=-1){

//开始返回授权文件信息

...

}

当我再连接服务器时,果然不出我所料,成功连接服务器。

但是连接过程有点慢,貌似此过程也正好是3秒钟,莫非在连接此服务器之前已经进行了另外一次连接,而且是失败的。

查看Adobe官网的资料才明白,flashplayer会在连接指定的端口之前连接目标主机的843端口,如果3秒后得不到授权文件才再向指定的端口去请求授权文件,如果再经过3秒还得不到授权文件的话,则断开连接,抛出securityError。

那 意思就是说在连接我的服务器的9999端口之前还连接了我的服务器的843端口,并且在843端口等待了3秒,没有得到授权文件,之后开始向我指定的端口 请求此授权文件。那好吧,既然你要了,我就给你吧,不给你的话你还再磨矶磨矶,于是我又在843端口开了一个ServerSocket,此处专门处理授权 文件的请求。

这时我再连接服务器,呵呵,特快专列(T843),立即就连接上了。

说明:貌似有很多客户机上的843端被禁用了,所以为了保险,需要在指定端口和843端都要能够处理授权文件的请求。

以下是示例程序及源文件下载:

http://remotedu.net/blog/attachments/month_0905/t2009549429.rar

lyx@lyx-virtual-machine:~/Codroid_ROS2_WS/src/codroid_msgs/src$ python3 2.py [INFO] [1761226713.090949430] [s360_robot_full_control]: === S3 60机器人控制节点启动 === [INFO] [1761226713.206587598] [s360_robot_full_control]: ✅ MoveIt2接口初始化完成 [INFO] [1761226713.207614623] [s360_robot_full_control]: 📡 Socket服务器监听: 192.168.101.111:8888 [INFO] [1761226713.208277726] [s360_robot_full_control]: ✅ 系统初始化完成: 192.168.101.111:8888 [INFO] [1761226713.209199884] [s360_robot_full_control]: 🚀 节点进入运行状态,等待指令... [INFO] [1761226718.977626025] [s360_robot_full_control]: 📞 新客户端连接: (&#39;192.168.101.112&#39;, 58502) [INFO] [1761226718.994709784] [s360_robot_full_control]: 📥 接收指令: 类型=p2, 参数=[[0.4, 0.1, 0.2], [1.0, 0.0, 0.0, 0.0]] [FATAL] [1761226719.002192131] [s360_robot_full_control]: ❌ 节点运行异常: Failed to get number of ready entities for action client: wait set index for result client is out of bounds, at ./src/rcl_action/action_client.c:635 Traceback (most recent call last): File "/home/lyx/Codroid_ROS2_WS/src/codroid_msgs/src/2.py", line 491, in main executor.spin() File "/opt/ros/humble/local/lib/python3.10/dist-packages/rclpy/executors.py", line 294, in spin self.spin_once() File "/opt/ros/humble/local/lib/python3.10/dist-packages/rclpy/executors.py", line 806, in spin_once self._spin_once_impl(timeout_sec) File "/opt/ros/humble/local/lib/python3.10/dist-packages/rclpy/executors.py", line 787, in _spin_once_impl handler, entity, node = self.wait_for_ready_callbacks( File "/opt/ros/humble/local/lib/python3.10/dist-packages/rclpy/executors.py", line 723, in wait_for_ready_callbacks return next(self._cb_iter) File "/opt/ros/humble/local/lib/python3.10/dist-packages/rclpy/executors.py", line 642, in _wait_for_ready_callbacks if wt in waitables and wt.is_ready(wait_set): File "/opt/ros/humble/local/lib/python3.10/dist-packages/rclpy/action/client.py", line 231, in is_ready ready_entities = self._client_handle.is_ready(wait_set) rclpy._rclpy_pybind11.RCLError: Failed to get number of ready entities for action client: wait set index for result client is out of bounds, at ./src/rcl_action/action_client.c:635 [INFO] [1761226719.002788258] [s360_robot_full_control]: 🛑 节点开始关闭,释放资源中... [INFO] [1761226719.003273958] [s360_robot_full_control]: ⏳ 等待Socket线程退出... [WARN] [1761226719.009362504] [s360_robot_full_control]: Joint states are not available yet! [INFO] [1761226719.010012613] [s360_robot_full_control]: Joint states are available now [INFO] [1761226719.072622565] [s360_robot_full_control]: 📋 收到规划轨迹: 37个点 [INFO] [1761226719.077731579] [s360_robot_full_control]: ⏳ 开始运动完成检测... [INFO] [1761226719.078853332] [s360_robot_full_control]: 📤 指令执行结果: None(客户端:(&#39;192.168.101.112&#39;, 58502)) Exception in thread Thread-2 (handle_client): Traceback (most recent call last): File "/usr/lib/python3.10/threading.py", line 1016, in _bootstrap_inner self.run() File "/usr/lib/python3.10/threading.py", line 953, in run self._target(*self._args, **self._kwargs) File "/home/lyx/Codroid_ROS2_WS/src/codroid_msgs/src/2.py", line 453, in handle_client self.get_logger().info(f"📞 客户端连接已断开: {client_socket.getpeername()}") OSError: [Errno 9] Bad file descriptor [INFO] [1761226719.981364921] [s360_robot_full_control]: 📡 Socket服务器已关闭 [INFO] [1761226719.982142669] [s360_robot_full_control]: 🗑️ 销毁MoveIt2资源... [INFO] [1761226719.988285992] [s360_robot_full_control]: ✅ 所有资源已释放 [INFO] [1761226720.018866697] [s360_robot_full_control]: ✅ 节点已完全关闭 #!/usr/bin/env python3 """ S3 60机器人控制节点 - 运动完成确认修复版 基于关节角度比较确认运动完成,到达目标后等待0.5s返回 修复:解决关节数据顺序问题、多线程资源竞争、节点关闭资源泄漏问题 """ import socket import json import time import traceback from threading import Thread, Lock import rclpy from rclpy.node import Node from rclpy.executors import MultiThreadedExecutor from sensor_msgs.msg import JointState from moveit_msgs.msg import DisplayTrajectory from pymoveit2 import MoveIt2 from trajectory_msgs.msg import JointTrajectory, JointTrajectoryPoint # -------------------------- 配置参数 -------------------------- # 请根据你的机器人硬件配置调整以下参数 JOINT_NAMES = ["Joint1", "Joint2", "Joint3", "Joint4", "Joint5", "Joint6"] BASE_LINK_NAME = "base_link" END_EFFECTOR_NAME = "link6" MOVE_GROUP_ARM = "arm" PLANNER_ID = "RRTConnectkConfigDefault" SERVER_IP = "192.168.101.111" # 机器人控制节点IP SERVER_PORT = 8888 # 通信端口 MAX_VELOCITY = 0.5 # 最大速度(单位:rad/s) MAX_ACCELERATION = 0.5 # 最大加速度(单位:rad/s²) JOINT_TOLERANCE = 0.03 # 关节角度容差(单位:rad) MAX_POINTS = 5 # 轨迹最大采样点数 MOTION_TIMEOUT = 30.0 # 运动超时时间(单位:s) class S360RobotControlNode(Node): def __init__(self): super().__init__("s360_robot_full_control") self.get_logger().info("=== S3 60机器人控制节点启动 ===") # 状态管理(新增:is_shutting_down用于关闭状态标记) self.is_shutting_down = False self.joint_state_received = False # 关节状态是否已接收 self.current_joint_positions = {name: 0.0 for name in JOINT_NAMES} # 关节位置字典 # 线程锁(新增:解决多线程资源竞争) self.joints_lock = Lock() # 关节数据锁 self.trajectory_lock = Lock() # 轨迹数据锁 self.moveit_lock = Lock() # MoveIt2操作锁(核心修复) # 轨迹与运动相关变量 self.planned_trajectory = None # -------------------------- 子系统初始化 -------------------------- # 1. 轨迹发布器(控制机器人运动) self.trajectory_pub = self.create_publisher(JointTrajectory, &#39;RobotMove&#39;, 10) # 2. 轨迹监控器(接收MoveIt2规划结果) self.trajectory_sub = self.create_subscription( DisplayTrajectory, &#39;/display_planned_path&#39;, self.trajectory_callback, 10 ) # 3. 关节状态订阅器(获取机器人当前关节位置) self.joint_state_sub = self.create_subscription( JointState, &#39;joint_states&#39;, self.joint_state_callback, 10 ) # 4. MoveIt2初始化(运动规划核心) self.moveit2 = None self.init_moveit2() # 5. Socket服务器(接收外部控制指令) self.socket_thread = Thread( target=self.start_socket_server, daemon=True ) self.socket_thread.start() self.get_logger().info(f"✅ 系统初始化完成: {SERVER_IP}:{SERVER_PORT}") # ======================= 核心初始化方法 ======================= def init_moveit2(self): """初始化MoveIt2接口,建立与运动规划器的连接""" try: self.moveit2 = MoveIt2( node=self, joint_names=JOINT_NAMES, base_link_name=BASE_LINK_NAME, end_effector_name=END_EFFECTOR_NAME, group_name=MOVE_GROUP_ARM ) self.moveit2.planner_id = PLANNER_ID self.moveit2.max_velocity = MAX_VELOCITY self.moveit2.max_acceleration = MAX_ACCELERATION self.get_logger().info("✅ MoveIt2接口初始化完成") except Exception as e: self.get_logger().error(f"❌ MoveIt2初始化失败: {e}") self.moveit2 = None # ======================= 数据回调方法 ======================= def joint_state_callback(self, msg): """关节状态回调:更新当前关节位置(字典存储,解决顺序问题)""" try: # 建立“关节名-位置”映射,避免顺序错乱 joint_mapping = {name: position for name, position in zip(msg.name, msg.position)} with self.joints_lock: # 只更新已知关节的位置,过滤无效数据 for name in JOINT_NAMES: if name in joint_mapping: self.current_joint_positions[name] = joint_mapping[name] self.joint_state_received = True # 标记关节数据已就绪 except Exception as e: self.get_logger().error(f"❌ 关节状态处理异常: {e}\n{traceback.format_exc()}") def trajectory_callback(self, msg): """轨迹回调:接收MoveIt2规划的轨迹点""" try: if msg.trajectory and msg.trajectory[0].joint_trajectory.points: with self.trajectory_lock: self.planned_trajectory = msg.trajectory[0].joint_trajectory.points self.get_logger().info(f"📋 收到规划轨迹: {len(self.planned_trajectory)}个点") except Exception as e: self.get_logger().error(f"❌ 轨迹处理异常: {e}") # ======================= 数据工具方法 ======================= def get_current_joints_in_standard_order(self): """获取标准顺序(JOINT_NAMES)的当前关节位置列表""" with self.joints_lock: return [self.current_joint_positions[name] for name in JOINT_NAMES] def wait_for_planned_trajectory(self, timeout=5.0): """等待MoveIt2规划轨迹生成,超时返回None""" start_time = time.time() while rclpy.ok() and not self.is_shutting_down: with self.trajectory_lock: if self.planned_trajectory: return self.planned_trajectory if time.time() - start_time > timeout: self.get_logger().warning("⚠️ 等待轨迹超时") return None time.sleep(0.1) return None # ======================= 轨迹发布方法 ======================= def publish_joint_trajectory(self, target_joints): """发布单目标点轨迹(用于简单关节运动)""" try: traj_msg = JointTrajectory() traj_msg.joint_names = JOINT_NAMES point = JointTrajectoryPoint() point.positions = target_joints point.time_from_start = rclpy.duration.Duration(seconds=2.0).to_msg() traj_msg.points.append(point) self.trajectory_pub.publish(traj_msg) return True except Exception as e: self.get_logger().error(f"❌ 关节轨迹发布失败: {e}") return False def publish_planned_trajectory(self, trajectory_points): """发布MoveIt2规划的多段轨迹(采样后发布,避免点数过多)""" try: if not trajectory_points: self.get_logger().warning("⚠️ 无轨迹点可发布") return False traj_msg = JointTrajectory() traj_msg.joint_names = JOINT_NAMES # 采样轨迹点(最多MAX_POINTS个,平衡平滑度与实时性) if len(trajectory_points) > MAX_POINTS: step = max(1, len(trajectory_points) // MAX_POINTS) trajectory_points = [trajectory_points[i] for i in range(0, len(trajectory_points), step)][:MAX_POINTS] # 分配时间戳,确保运动平滑 total_time = 5.0 # 总运动时长(可根据需求调整) for i, point in enumerate(trajectory_points): traj_point = JointTrajectoryPoint() traj_point.positions = point.positions traj_point.time_from_start = rclpy.duration.Duration( seconds=total_time * (i / (len(trajectory_points) - 1)) ).to_msg() traj_msg.points.append(traj_point) self.trajectory_pub.publish(traj_msg) return True except Exception as e: self.get_logger().error(f"❌ 规划轨迹发布失败: {e}") return False def publish_smooth_trajectory(self, start_joints, target_joints, num_points=5): """发布平滑过渡轨迹(从起点到目标点的线性插值)""" try: traj_msg = JointTrajectory() traj_msg.joint_names = JOINT_NAMES num_points = min(num_points, MAX_POINTS) # 限制最大点数 total_time = 5.0 # 总运动时长 # 线性插值生成中间点 for i in range(num_points): ratio = i / (num_points - 1) if num_points > 1 else 1.0 point = JointTrajectoryPoint() point.positions = [ start + ratio * (target - start) for start, target in zip(start_joints, target_joints) ] point.time_from_start = rclpy.duration.Duration(seconds=total_time * ratio).to_msg() traj_msg.points.append(point) self.trajectory_pub.publish(traj_msg) return True except Exception as e: self.get_logger().error(f"❌ 平滑轨迹发布失败: {e}") return False # ======================= 运动控制核心方法 ======================= def execute_joint_motion(self, target_joints): """执行关节空间运动:根据目标关节角度控制机器人""" # 修复1:关闭状态下拒绝执行 if self.is_shutting_down: self.get_logger().error("❌ 节点正在关闭,拒绝关节运动指令") return False # 修复2:检查关节数据是否就绪 if not self.joint_state_received: self.get_logger().error("❌ 未收到关节状态数据,无法执行关节运动") return False # 基础参数校验 if not self.moveit2 or len(target_joints) != len(JOINT_NAMES): self.get_logger().error("❌ 关节运动参数无效(MoveIt2未就绪或关节数不匹配)") return False try: # 获取当前关节位置,用于生成平滑轨迹 current_joints = self.get_current_joints_in_standard_order() self.publish_smooth_trajectory(current_joints, target_joints) # 修复3:MoveIt2操作加锁,避免多线程竞争 with self.moveit_lock: self.moveit2.move_to_configuration(target_joints) # 等待运动完成(基于关节角度容差判断) return self.wait_for_motion_completion(target_joints, timeout=MOTION_TIMEOUT) except Exception as e: self.get_logger().error(f"❌ 关节运动异常: {e}\n{traceback.format_exc()}") return False def execute_pose_motion(self, target_pose): """执行位姿空间运动:根据目标位姿(位置+姿态)控制机器人""" # 修复1:关闭状态下拒绝执行 if self.is_shutting_down: self.get_logger().error("❌ 节点正在关闭,拒绝位姿运动指令") return False # 修复2:检查关节数据是否就绪 if not self.joint_state_received: self.get_logger().error("❌ 未收到关节状态数据,无法执行位姿运动") return False # 基础参数校验(target_pose格式:[[x,y,z], [x,y,z,w]]) if not self.moveit2 or len(target_pose) != 2 or len(target_pose[0]) != 3 or len(target_pose[1]) != 4: self.get_logger().error("❌ 位姿运动参数无效(格式错误或MoveIt2未就绪)") return False try: target_pos, target_quat = target_pose # 拆分位置和姿态 # 修复3:MoveIt2操作加锁,避免多线程竞争 with self.moveit_lock: # 1. 计算逆运动学(位姿→关节角度) ik_result = self.moveit2.compute_ik(target_pos, target_quat) if not ik_result: self.get_logger().error("❌ 逆运动学求解失败(目标位姿不可达)") return False # 2. 执行位姿运动 self.moveit2.move_to_pose(position=target_pos, quat_xyzw=target_quat, cartesian=False) # 3. 提取目标关节角度(用于运动完成判断) target_joints = self._extract_joint_positions_from_state(ik_result) if not target_joints: self.get_logger().error("❌ 无法提取目标关节角度,无法判断运动完成") return False # 4. 发布轨迹(优先用规划轨迹,其次用平滑轨迹) planned_trajectory = self.wait_for_planned_trajectory(timeout=5.0) if planned_trajectory: self.publish_planned_trajectory(planned_trajectory) else: current_joints = self.get_current_joints_in_standard_order() self.publish_smooth_trajectory(current_joints, target_joints) # 5. 等待运动完成 return self.wait_for_motion_completion(target_joints, timeout=MOTION_TIMEOUT) except Exception as e: self.get_logger().error(f"❌ 位姿运动异常: {e}\n{traceback.format_exc()}") return False def _extract_joint_positions_from_state(self, joint_state): """从JointState消息中提取标准顺序的关节位置""" if not joint_state or not hasattr(joint_state, &#39;position&#39;): self.get_logger().warning("⚠️ 无效的JointState消息,无法提取关节位置") return None try: position_map = {name: pos for name, pos in zip(joint_state.name, joint_state.position)} # 按JOINT_NAMES顺序提取,确保与目标顺序一致 return [position_map[name] for name in JOINT_NAMES if name in position_map] except Exception as e: self.get_logger().error(f"❌ 关节提取失败: {e}") return None # ======================= 运动完成判断方法 ======================= def wait_for_motion_completion(self, target_joints, timeout=30.0): """基于关节角度容差判断运动是否完成(稳定0.5s后确认)""" start_time = time.time() target_reached_time = None # 首次到达目标的时间 last_log_time = start_time # 上次打印日志的时间 stable_count = 0 # 稳定计数器(0.1s/次,5次=0.5s) self.get_logger().info("⏳ 开始运动完成检测...") while rclpy.ok() and not self.is_shutting_down: # 1. 检查超时 if time.time() - start_time > timeout: current_joints = self.get_current_joints_in_standard_order() errors = [abs(c - t) for c, t in zip(current_joints, target_joints)] self.get_logger().error( f"⚠️ 运动超时! 最大误差: {max(errors):.4f}rad\n" f"目标关节: {[round(t, 3) for t in target_joints]}\n" f"当前关节: {[round(c, 3) for c in current_joints]}" ) return False # 2. 获取当前关节位置 current_joints = self.get_current_joints_in_standard_order() # 3. 检查是否到达目标(所有关节在容差内) if self._joints_reached_target(current_joints, target_joints): if target_reached_time is None: target_reached_time = time.time() self.get_logger().info("🎯 首次到达目标位置,开始稳定检测...") stable_count += 1 # 稳定0.5s(5个检测周期)后确认完成 if stable_count >= 5: stable_time = time.time() - target_reached_time self.get_logger().info(f"✅ 运动完成! 稳定时间: {stable_time:.2f}s") return True else: # 未到达目标,重置稳定计数器 target_reached_time = None stable_count = 0 # 4. 每秒打印一次运动状态(避免日志刷屏) current_time = time.time() if current_time - last_log_time >= 1.0: last_log_time = current_time elapsed = current_time - start_time errors = [abs(c - t) for c, t in zip(current_joints, target_joints)] self.get_logger().info( f"⏱️ 运动中... 已耗时: {elapsed:.1f}s | " f"最大误差: {max(errors):.4f}rad | " f"稳定计数: {stable_count}/5" ) time.sleep(0.1) # 100ms检测周期,平衡实时性与资源占用 def _joints_reached_target(self, current_joints, target_joints): """检查所有关节是否在容差范围内到达目标位置""" return all(abs(c - t) <= JOINT_TOLERANCE for c, t in zip(current_joints, target_joints)) # ======================= 网络通信方法 ======================= def start_socket_server(self): """启动Socket服务器,接收外部控制指令(如p1/p2/p3)""" server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM) server_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) # 允许端口复用 try: server_socket.bind((SERVER_IP, SERVER_PORT)) server_socket.listen(5) # 最大同时连接数 server_socket.settimeout(1.0) # 超时避免阻塞关闭流程 self.get_logger().info(f"📡 Socket服务器监听: {SERVER_IP}:{SERVER_PORT}") while rclpy.ok() and not self.is_shutting_down: try: # 接收客户端连接(非阻塞,超时后循环检查关闭状态) client_socket, addr = server_socket.accept() self.get_logger().info(f"📞 新客户端连接: {addr}") # 启动线程处理客户端请求(避免阻塞其他连接) Thread(target=self.handle_client, args=(client_socket,), daemon=True).start() except socket.timeout: continue # 超时无连接,继续循环检查关闭状态 except Exception as e: self.get_logger().error(f"❌ Socket连接异常: {e}") except Exception as e: self.get_logger().fatal(f"❌ Socket服务器启动失败: {e}") finally: server_socket.close() self.get_logger().info("📡 Socket服务器已关闭") def handle_client(self, client_socket): """处理单个客户端的控制指令(指令格式:cmd_type:json_params)""" try: # 接收指令(最大1024字节,避免数据包过大) data = client_socket.recv(1024).decode().strip() if not data: self.get_logger().warning("⚠️ 客户端发送空指令,断开连接") return # 解析指令格式(如"p2:[[0.4,0.1,0.2],[1.0,0.0,0.0,0.0]]") if &#39;:&#39; not in data: self.get_logger().error(f"❌ 无效指令格式: {data}(需符合&#39;cmd:params&#39;)") client_socket.sendall("False".encode()) return cmd_type, cmd_json = data.split(&#39;:&#39;, 1) cmd_params = json.loads(cmd_json) self.get_logger().info(f"📥 接收指令: 类型={cmd_type}, 参数={cmd_params}") # 执行对应指令 success = False if cmd_type == "p1": # p1指令:关节空间运动(参数:[joint1, joint2, ..., joint6]) success = self.execute_joint_motion(cmd_params) elif cmd_type in ["p2", "p3"]: # p2/p3指令:位姿空间运动(参数:[[x,y,z], [x,y,z,w]]) success = self.execute_pose_motion(cmd_params) else: self.get_logger().error(f"❌ 未知指令类型: {cmd_type}(支持p1/p2/p3)") # 向客户端返回执行结果(True/False) client_socket.sendall(str(success).encode()) self.get_logger().info(f"📤 指令执行结果: {success}(客户端:{client_socket.getpeername()})") except json.JSONDecodeError: self.get_logger().error(f"❌ JSON参数解析失败: {cmd_json}") client_socket.sendall("False".encode()) except Exception as e: self.get_logger().error(f"❌ 客户端处理异常: {e}\n{traceback.format_exc()}") client_socket.sendall("False".encode()) finally: client_socket.close() self.get_logger().info(f"📞 客户端连接已断开: {client_socket.getpeername()}") # ======================= 节点关闭方法 ======================= def shutdown(self): """优化关闭流程:确保线程退出、资源释放(核心修复)""" self.is_shutting_down = True self.get_logger().info("🛑 节点开始关闭,释放资源中...") # 1. 等待Socket线程退出(避免通信中强制关闭) if hasattr(self, &#39;socket_thread&#39;) and self.socket_thread.is_alive(): self.get_logger().info("⏳ 等待Socket线程退出...") self.socket_thread.join(timeout=2.0) # 最多等待2秒 if self.socket_thread.is_alive(): self.get_logger().warning("⚠️ Socket线程未正常退出,强制终止") # 2. 销毁MoveIt2资源(避免资源泄漏导致的"Destroyable"错误) if hasattr(self, &#39;moveit2&#39;) and self.moveit2 is not None: self.get_logger().info("🗑️ 销毁MoveIt2资源...") # 置空MoveIt2实例,触发垃圾回收 self.moveit2 = None # 3. 关闭发布器与订阅器(ROS2节点标准操作) self.destroy_publisher(self.trajectory_pub) self.destroy_subscription(self.trajectory_sub) self.destroy_subscription(self.joint_state_sub) self.get_logger().info("✅ 所有资源已释放") def main(): """节点主函数:初始化ROS2、启动执行器""" rclpy.init() node = S360RobotControlNode() # 使用多线程执行器,支持同时处理回调与Socket通信 executor = MultiThreadedExecutor(num_threads=4) executor.add_node(node) try: node.get_logger().info("🚀 节点进入运行状态,等待指令...") executor.spin() except KeyboardInterrupt: # 捕获Ctrl+C中断信号 node.get_logger().info("🛑 收到键盘中断信号(Ctrl+C)") except Exception as e: # 捕获其他未知异常 node.get_logger().fatal(f"❌ 节点运行异常: {e}\n{traceback.format_exc()}") finally: # 确保关闭流程执行 node.shutdown() node.destroy_node() rclpy.shutdown() node.get_logger().info("✅ 节点已完全关闭") if __name__ == "__main__": main() 怎么解决,给我完整的解决代码
最新发布
10-24
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值