rosnode中使用xmlrpc建立topic之间链接的一些知识点

rosmaster是一个中转。

每个node都是从roscpp中继承了一堆东西。

1. topic的消息如何来的

当一个nodesubscribe一个topic时,该node就会建立一个subscription结构。含有一个subscriptionQueue,用来保存收到的msg.

谁往queue里面填冲消息?



首先了解一下TransportPublisherLink, 对于一个clientnode来说,他说subscribetopic有很多publisher,他们可能是别的进程,TransportPublisherLink就是他们在clientnode进程内部的一个代表。

TransportPublisherLink来了数据,当那些publisher向该topic发送消息时,本clientnode对应的TransportPublisherLink::onMessageLength就被触发了,说明来数据了:



->TransportPublisherLink::onMessageLength,callback是绑定在一个connection上的。当该transportconnection上有数据时,就会调用此callback.

->TransportPublisherLink::onMessage,继而会触发此函数

->TransportPublisherLink::handleMessage,publisher_link的作用就是收到消息后转发给关联的subscription.

->Subscription::handleMessage,这个函数将该消息放入subscriptionqueue, 并且往callbackQuque里面插入一个callback,callback会异步的被callbackQuqueManagerthread处理,callbackQueueManager维护一些workerthread, 这些workerthread会从callbackQueue里面取出一个callback执行,执行时,会从subscriptionqueue里面取出消息数据来处理。

->SubscriptionQueue::pushcallback_queue_->addCallback

2 TransportPublisherLink的建立

好了,知道了,是TransportPublisherLink受到消息后,向subscriptionQueuecallbackQueue里面插入信息了,那么TransportPublisherLink怎么建立的,他如何收到消息的?



我们根据movebase的一个/move_base_simple/goaltopic来说起。

Move base node subscribetopic,而运行于pc上的rviz会向该topic发送消息。用来告诉movebase目的地。

大体流程是:

1. move base启动后subscribe到该topic,此时rviz还没有启动。movebase node 会见里subscription结构

2. rviz启动后,rviz会链接到rosmaster

3. rosmaster 得知rvizadvertisetopic,于是就会使用python实现的xmlrpc远程调用接口调用publisherUpdate来通知clientnodepuhlisher更新。

4. client node这边收到xmlrpc的远程过程调用,会调用绑定在该remoemethod上的callback.

TopicManager::pubUpdateCallback(XmlRpc::XmlRpcValue& params,XmlRpc::XmlRpcValue& result),这个callback是通过xmlrpc_manager_->bind("publisherUpdate",boost::bind(&TopicManager::pubUpdateCallback, this, _1, _2));绑定的。那么这个callback干了什么?这样clientnode就知道有publisher来了。才能和rviz建立通知,并且clientnode能够知道来者的ipport,以便于直接和publisher建立p2p链接,而不用rosmaster中转。

这个callback

TopicManager::pubUpdate(const string&topic, const vector<string> &pubs)会根据topic找到对应的subscription并将新的publisher列表告诉subscription.

->Subscription::pubUpdate() subscription首先检查新来的publisher是否已经建立链接了,如果没有,会开始建立链接:

->Subscription::negotiateConnection这里尝试和新的publisher协商,并建立一个pendingConnection.当协商结束,pendingConnectioncallback会被调用,最后成为一个正常的connection.

具体过程是:首先建立一个XmlRpcClient(peer_hostpeer_port,"/", );然后调用他的XmlRpcClient::executeNonBlock(constchar* method, params)执行”requestTopic”这个rpc.这个过程是异步的。会立刻返回。那么这个远程过程调用就会发给rviz, rviz接受后,就有结果回来,就会有异步的callback通知给xmlRpcClient.

再具体一点xmlRpcClientexecuteNonBlock先建立和rviz的链接,建立socket,然后建立异步connect.然后prepare一份request,这个request包含了要被远程调用的method和参数。但是并不马上发送。有专门的角色完成该工作。此时,xmlRpcClient维护了一个socket,并且也有一份request数据。,他把自己注册给XmlRpcDispatch,由xmlRpcDispatchwork()函数负责检查该fd有没有数据io,并处理数据。检查工作在一个xmlRPCManager的线程中执行:

XMLRPCManager::serverThreadFunc()

-XmlRpcServer::work,简单调用xmlRpcDispatch::work

-XmlRpcDispatch::work,dispatcher维护一个sourcelist, select各个xmlrpcsourcefd,看有没有读写事件发生,如果有,就掉用sourcehandleEvent处理。xmlrpcsource实际上封装了一个socketfd. xmlRpcClient就是一个xmlRpcSource,并且被注册进来了。

-XmlRpcClient::handleEvent,这个函数会发送request,也会接受response.

->writeResponse

->XmlRpcSocket::nbWrite



上面提到,Subscription::negotiateConnection会建立pendingConnection,这些connection会被添加到XMLRPCManager::instance()->addASyncConnection(conn);里面。



XMLRPCManager::serverThreadFunc()除了负责触发socketio,还负责不断检查pendingconnection是否已经完成。



voidXMLRPCManager::serverThreadFunc()这个函数是一个循环,他不断检查所有的connection,调用他们的check()函数:

-PendingConnection::check(),检查pendingConnection是否已经ok,已经建立链接,如果是就回调subscription。而check实际是通过检查XmlRpcClient::executeCheckDone(XmlRpcValue&result)函数来实现的,该函数实际上检查connectionState是否_IDLE,那么connectionState何时成为_IDLE?当然是收到了request的结果了。我们知道XmlRpcCLient::handleEvent会处理socketio,如果收到了结果,继而会调用到XmlRpcClient::readResponse(),这个函数就会把connectionState设置为_IDLE.

一旦pendingConnection被检查发现完成了工作,就掉用:

->void Subscription::pendingConnectionDone(constPendingConnectionPtr& conn, XmlRpcValue&result),这个函数会建立如下几个物体:

TransportTCPPtr transport(newTransportTCP(&PollManager::instance()->getPollSet()));

if (transport->connect(pub_host,pub_port)) //链接topic.

{

ConnectionPtr connection(newConnection()); //connection对象

TransportPublisherLinkPtr pub_link(newTransportPublisherLink(name_,shared_from_this(), xmlrpc_uri,transport_hints_));



connection->initialize(transport, false,HeaderReceivedFunc());

pub_link->initialize(connection);



ConnectionManager::instance()->addConnection(connection);



boost::mutex::scoped_lock lock(publisher_links_mutex_);

addPublisherLink(pub_link);

}

可见,这里就是建立了connection了。

3 后面有时间可以看看connectionConnectionManagerTransportTCP的管理



评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值