Ros1中的pub和sub是如何建立连接的?
先上时序图,一睹为快
连接拓扑关系
简要描述连接拓扑关系:
pub<–>rosmaster: pub节点进程与rosmaster使用xmlrpc机制进程通讯,xmlrpc是基于xml格式和http协议的一种rpc(remote procedure call)。
sub<–>rosmaster: 同pub节点。
pub<–>sub: 节点间使用socket(tcp/udp传输层协议)进行连接
详细流程探究
方法
源码: 下载ros源码并编译(可参考上篇文章),也可以直接使用ros wiki链接进行操作。
断点调试:
- 节点连接主要是在ros_comm/roscpp,所以修改roscpp下的CMakeLists文件,增加编译debug选项(add_definitions("-Wall -g"))。
- 编译源码,将libroscpp.so放到的你ros环境的lib目录下(例如:/opt/ros/melodic/lib)。
- 在ros的tutorial中编写talker、listener示例(注意两个的CMakeLists也要增加add_definitions("-Wall -g"))。
- gdb运行talker、listener。
流程描述
talker主要代码:
ros::init(argc, args, "talker");
ros::NodeHandle n;
ros::Publisher pub = n.advertise<talker::Num>("Num", 1000);
listener主要代码
ros::init(argc, args, "listener");
ros::NodeHandle n;
ros::Subscriber sub = n.subscribe("Num", 1000, NumCallback);
可以发现相同的代码为ros::init()、ros::NodeHandle n;
ros::init()流程主要是进行命名空间的mapping、注册退出回调、ros::console日志模块初始化,network初始化(主要根据传参来设置host、tcpros_server_port端口)、master初始化(主要根据传参和环境变量来设置master的uri)、this_node的初始化,file_log的初始化,param的初始化,这部分抽时间单独写一篇。
ros::NodeHandle n; 包含了NodeHandle的构造函数,在NodeHandle的构造函数里调用了construct()内部函数,然后执行ros::start(),启动整个通信框架(这里有个意思的点是,使用引用计数来统计NodeHandle构造的次数(构造函数中++,析构函数–,这个引用计数属于文件对象隶属于node_handle.cpp),首次构造会进行ros::start,析构函数中当引用计数为0时进行ros::shutdown;
比较重要的时ros::start(), 启动ros整个通信框架,主要启动TopicManager、PollManager、ConnectionManager, XMLRPCManager.
ConnectionManager.start: 启动tcpros的服务端执行(执行tcpros的bind,listen,此处服务端bind的port值为0,让kernel自动分配端口,然后保存端口,当sub上线的时候通过xmlrpc经过rosmaster将端口发送到给sub,然后sub进行connect连接)。
Q1: tcpserver的port端口产生?
A1: 端口默认是从入参中__tcpros_server_port=xxx获得,如果启动时无该参数,则port为0,此时使用listen监听socket时,内核会自动分配端口,随后通过getsockname获取自动分配的port。
Q2: tcpserver的ip地址怎么产生?
A2: network::getHost获取,默认从入参__hostname=xxx获得,如果启动参数无该参数,则使用gethostname获取本端ip地址。
Q3: tcpclient如果获取服务端的ip和端口?
A3: 首先对于pub/sub来讲,pub属于tcpserver端,sub属于tcpclient端;tcpserver在nodehandle构造的时候进行listen启动;tcpclient端什么时候进行connect呢,只有当pub和sub配对之后才进行(配对意味着pub执行了advertise,sub执行了subscribe); 那又分为两种情况,第一种情况先advertise后subscribe,此时sub进行subscribe会使用requestTopic的xmlrpc调用至rosmaster,rosmaster通过xmlrpc调用至pub,最终将pub的port和ip返回至sub,然后进行connect;
第二种情况,先subscribe再advertise,此时pub进行advertise后,会触发pubupdate(传递pub的port和ip),然后使用xmlrpc远程调用至rosmaster,rosmaster然后通过xmlrpc远程调用sub,进而sub注册的回调函数被调用,在回调函数中执行connect;