节点间通过XMLRPC建立连接
在一个节点刚启动的时候,它并不知道其它节点的存在,更不知道它们在交谈什么,当然也就谈不上。
所以,它要先与master对话查询其它节点的状态,然后再与其它节点通信。
而节点与master对话使用的就是XMLRPC。
从这一点来看,master叫节点管理器确实名副其实,它是一个大管家,给刚出生的节点提供服务。
下面我们以两个节点:talker和listener为例,介绍其通过XMLRPC建立通信连接的过程,如下图所示。
- talker注册
假设我们先启动talker。启动后,它通过1234使用XMLRPC向master注册自己的信息,包含所发布消息的名。master会将talker的注册信息加入注册列表中;
2.listener注册
listener启动后,同样通过XMLRPC向master注册自己的信息,包含需要订阅的话题名;
3.master进行匹配
master根据listener的订阅信息从注册列表中查找,如果没有找到匹配的发布者,则等待发布者的加入,如果找到匹配的发布者信息,则通过XMLRPC向listener发送talker的地址信息。
4.listener发送连接请求
listener接收到master发回的talker地址信息,尝试通过XMLRPC向talker发送连接请求,传输订阅的话题名、消息类型以及通信协议(TCP或者UDP);
5.talker确认连接请求
talker接收到listener发送的连接请求后,继续通过XMLRPC向listener确认连接信息,其中包含自身的TCP地址信息;
6.listener尝试与talker建立连接
listener接收到确认信息后,使用TCP尝试与talker建立连接。
7.talker向listener发布消息
成功建立连接后,talker开始向listener发送话题消息数据,master不再参与。
从上面的分析中可以发现,前五个步骤使用的通信协议都是XMLRPC,最后发布数据的过程才使用到TCP。
master只在节点建立连接的过程中起作用,但是并不参与节点之间最终的数据传输。
节点在请求建立连接时会通过masr.cpp文件中的execute()函数调用XMLRPC库中的函数。
我们举个例子,加入talker节点要发布消息,它会调用toc_manager.cpp中的TopicManager::verse()函数,在函数中会调用execute()函数,该部分代码如下。
XmlRpcValue args, result, payload;
args[0] = this_node::getName();
args[1] = ops.topic;
args[2] = ops.datatype;
args[3] = xmlrpc_manager_- >getServerURI();
master::execute("registerPublisher", args, result, payload, true);
其中,registerPublisher就是一个远程过程调用的方法(或者叫函数)。节点通过这个远程过程调用向master注册,表示自己要发布发消息了。
你可能会问,registerPublisher方法在哪里被执行了呢?我们来到_comm-noet-develtoolsrosmastercrosmaster路径下,打开master_api.py文件,然后搜索registerPublisher这个方法,就会找到对应的代码,如下。
匆匆扫一眼就知道,它在通知所有订阅这个消息的节点,让它们做好接收消息的准备。
你可能注意到了,这个被调用的XMLRPC是用语言实现的。
也就是说,XMLRPC通信时只要报文的格式是一致的,不管还是python语言,都可以实现远程调用的功能。
def registerPublisher(self, caller_id, topic, topic_type, caller_api):
try:
self.ps_lock.quire()
self.reg_manager.register_publisher(topic, caller_id, caller_api)
# don't let '*' type squash valid typing
if topic_type != rosgraph.names.ANYTYPE not topic in self.topics_types:
self.topics_types[topic] = topic_type
pub_uris = self.publishers.get_apis(topic)
_uris = self.subscribers.get_apis(topic)
self._notify_topic_subscribers(topic, pub_uris, sub_uris)
mloginfo("+PUB [%s] %s %s",topic, caller_id, caller_api)
sub_uris = self.subscribers.get_apis(topic)
finally:
self.ps_lock.release()
return 1, "Registered [%s] as publisher of [%s]"%(caller_id, topic), s