总体来说,代码分2个部分:后台的DAEMON和暴露给上层的接口。 其中DAEMON我们可以看成是一条后台BUS,负责与应用通信和管理远程连接; 我们可以用暴露出来的上层接口编写Server或Client应用挂到后台BUS上并通信。
熟悉对象几个重要的类和对象:Busattachment Bus Internal BusController BusObject ALLJoynObj DbusObj ProxyBusObject Router DaemonRouter ClientRouter Transport TransportList DaemonTransport ClientTransport BusEndpointLocalEndpoint RemoteEndpoint TCPEndpoint BTEndpoint DaemonEndpoint ClientEndpoint InterfaceDescription
DAEMON和上层接口(就是两个Busattachment对象之间的通信)
最TOP的对象是Busattachment,我们看注释:BusAttachment isthe top-level object responsible for connecting to and optionally managing amessage bus. 这个BusAttachment非常重要,它有两个构造函数,即作DAEMON和上层的接口。 作DAEMON时我们称为daemonconstructor,体现注释里面connect 和 manage; 作应用接口时我们称为clientconstructor,只体现connect.
在DAEMON部分,TOP的两个对象是BusController 和继承于Busattachment的BUS。其中BUS下面有BusEndpoint来负责数据传输。
LocalEndpoint
BUS(Busattachment)-----Internal-----DaemonRouter DaemonEndpoint
RemopeEndpoint TCPEndpoint
BTEndpoint
VirtualEndpoint
setbuscontroller
&Bus
BusController Dbusobj(BusObject)---components---InterfaceDescription,MethodContext
Alljoynobj(BusObject)---components---InterfaceDescription,MethodContext
在上层接口部分,只有Busattachment
Busattachment-----Internal-----ClientRouter--- LocalEndpoint
ClientEndpoint
DAEMON和上层接口通过Endpoint(基于QCC_AF_UNIXSocket) 通信,下面我们看
LocalEndpoint,
MethodTable
LocalEndpoint ProxyBusObject* dbusObj
ProxyBusObject*alljoynObj
在DAEMON和上层接口都有自己的LocalEndpoint,上层接口访问ProxyBusObject*dbusObj
中的代理函数,会由CallMsg-MarshalMessage-PushMessage组包并通过ClientEndpoint发送给DAEMON的DaemonEndpoint。
DAEMON则由HandleMethodCall来处理,处理函数在MethodTable中。 (上面提到的Dbusobj中的各个Method等会在registerbusobject时install到LocalEndpoint的MethodTable)
同样上层接口可能通过RemopeEndpoint - PushMessage 到远端的平台上的DAEMON。
DAEMON初始化流程
从JNI的DaemonMain函数开始,
DaemonMain
DaemonConfig
daemon
transportList
Bus ajBus();构造Busattachment-Internal- localEndpoint
……
DBus::CreateInterfaces ifaceDescriptions
alljoyn::CreateInterfaces
BusControrller ajBusController();构造BusControrller-dbusObj
-alljoynObj
BusControrller.init
dbusObj.Init add InterfaceDescription的handle到components
bus.RegisterBusObject
LocalEndpoint::RegisterBusObject
LocalEndpoint::OnBusConnected AddAlarm
DeferredCallbacks::AlarmTriggered trigger
DBusObj::ObjectRegistered
busController->ObjectRegistered
AllJoynObj::Init add InterfaceDescription
dispatcher.Start
ProxyBusObject* dbusObj(proxy Interface)
ProxyBusObject*alljoynObj(proxy Interface)
BusAttachment::Start LocalEndpoint::Start
DaemonTransport::Start
busInternal->transportList.Start TCPTransport::Start
BTTransport::Start
router.RegisterEndpoint
Thread::Start->TcpTransport::run->while/socket.accept
bus.StartListen
TCPTransport::StartListen alert
DaemonTransport::StartListen ("unix:abstract=alljoyn":listen本地上层接口Busattachment)
BTTransport::StartListen
上层接口的Busattachment
Busattachment的client初始化比较简单。
newBusAttachment
Busattachment构造Busattachment-Internal- ClientTransport
BusAttachment::Start
busInternal->transportList.Start
LocalEndpoint::Start ProxyBusObject* dbusObj -RemoteEndpoint*b2bEp
ProxyBusObject* alljoynObj
BusAttachment::Connect
ClientTransport::Connect
Socket(QCC_AF_UNIX,QCC_SOCK_STREAM, sockFd)
m_endpoint->Start->RemoteEndpoint::Start-> ClientRouter::RegisterEndpoint
关于BLUETOOTH的ENDPOINT的一点说明
前面讲DAEMON和应用都是一个BUS或Busattachment,但是如果加入BT ENDPOINT时,和BLUEZ通信,所以DAEMON中包含一个BT的Busattachmentclient, 见BTAccessor::BTAccessor中bzBus("BlueZTransport")。
另外,DAEMON要和BLUEZ建立联系,所以DAEMON应包含BLUEZ的权限,setuid(BLUETOOTH_UID);
代码目录
我们涉及到的代码主要在/alljoyn/core/src /alljoyn/core/daemon /common/src /common/os/posix这几个目录下面,其中/common/os/posix 是基于我们平台的定时器Timer,互斥Mutex,线程Thread ,socket, event; /common/src主要是XML配置解析, LOG设置 ,string, 以及对/common/os/posix中的Thread ,socket部分封装;
/alljoyn/core/daemo/和/alljoyn/core/src就是协议的主要部分,其中上层接口的Busattachment对象只涉及/alljoyn/core/src的代码,而DAEMON涉及/alljoyn/core/src和/alljoyn/core/daemon两个部分的代码。
我们看/alljoyn/core/src下面的主要文件BusAttachment.cc ProxyBusObject.cc LocalTransport.cc BusObject.cc Message.cc Message_Parse.cc Message_Gen.cc MsgArg.cc SignalTable.cc BusEndpoint.cc ClientRouter.cc ClientTransport.cc RemoteEndpoint.cc Transport.cc TransportList.cc Auth***.cc
BusAttachment.cc是总的对象入口,上层接口主要调用这里面的接口;
BusObject.cc ProxyBusObject.cc远程的代理,从ProxyBusObject访问远程接口。
Message.cc Message_Parse.cc Message_Gen.cc MsgArg.cc是两个ENDPOINT之间的消息数据的打包解包。
其他的文件包含Transport或Endpoint字符信息的函数都是负责物理数据包的发送接收。
Auth打头的文件是安全验证接口。
/alljoyn/core/daemo/主要是DAEMON特有的接口。AllJoynObj.cc Bus.cc BusController.cc
DaemonRouter.cc DaemonTransport.cc DBusObj.cc NameTable.cc TCPTransport.cc VirtualEndpoint.cc daemon-main.cc Packet.cc BT***.cc P2P***.cc
我们BusAttachment DAEMON比BusAttachment Client看多出来的就是BusController和DBusObj, AllJoynObj。Bus.cc是BusAttachment 做DAEMON时的实现,AllJoynObj.cc和DBusObj.cc是BUS提供的服务的具体哪些函数的实现。
daemon-main.cc是DAEMON的初始化入口,Packet.cc是ENDPOINT的数据打包。
DaemonTransport.cc是和上层接口BusAttachment Client的通信ENDPOINT。
TCPTransport.cc是和远程平台上DAEMON的通信ENDPOINT。WIFI基于此ENDPOINT。
BT和WIFI DIRECT ENDPOINT的函数较多。
当然DAEMON包括/alljoyn/core/src下面的函数,主要功能也是和上面一致的。
/alljoyn/core/daemo/ns这下面的几个函数主要负责物理上远程DAEMON的UDP探测。
一次调用流程(上层应用与DAEMON的通信)
上层应用和DAEMON之间需要通信,下面我们看从上层接口和DAEMON通过SOCKET的一次完整调用流程,比如从BusAttachment::RequestName到DBusobj里面的函数原型RequestName:
CLIENT: BusAttachment::RequestName -> dbusObj.MethodCall ->
bus->GetInternal().GetRouter().PushMessage ->ClientRouter::PushMessage -> nonLocalEndpoint->PushMessage -> RemoteEndpoint::PushMessage -> txQueue.push_front
------(Thread switch)> RemoteEndpoint::TxThread::Run ->msg->Deliver -> sink.PushBytes -> SocketStream::PushBytes-> Send(套接字接口)
DAEMON: Recv -> SocketStream::PullBytes ->source.PullBytes -> msg->Unmarshal->RemoteEndpoint::RxThread::Run ->router.PushMessage ->DaemonRouter::PushMessage
->LocalEndpoint::PushMessage->LocalEndpoint::DoPushMessage ->LocalEndpoint::HandleMethodCall ->entry->object->CallMethodHandler->DBusObj::RequestName
DISCOVERY (DAEMON与DAEMON通信)
不光应用与DAEMON之间需要通信,DAEMON与远程的DAEMON也需要通信,下面我们看这个DAEMON之间的通信,在应用调用BusAttachment::FindAdvertisedName时会查找远程DAEMON上的服务(我们假设应用分别在两台不同宿主机上)。首先从应用的:FindAdvertisedName到DAEMON内的AllJoynObj::FindAdvertisedName同上述的应用与DAEMON通信流程一样,不再叙述。接着DAEMON的AllJoynObj ::FindAdvertisedName会查找内部transList.GetTransport,依次调用EnableDiscovery,以下都以TCP为例。
AllJoynObj::FindAdvertisedName -> trans->EnableDiscovery -> TCPTransport::EnableDiscovery -> TCPTransport::QueueEnableDiscovery-> m_listenRequests.push(listenRequest)------(aler:thread switch)> TCPTransport::RunListenMachine->TCPTransport::EnableDiscoveryInstance
DoStartListen:createsocket and listen port 9955
IpNameService::FindAdvertisedName-> IpNameServiceImpl::QueueProtocolMessage--
---alert->IpNameServiceImpl::Run -> LazyUpdateInterfaces() -> Socket(这里是UDP,有类似IFCONFIG命令,发送到远程9956端口,探测)
IpNameServiceImpl::SendOutboundMessages-> SendProtocolMessage -> SendTo(用上面的UDP port9956发送探测)
RecvFrom -> HandleProtocolMessage-> TCPTransport::FoundCallback::Found
注:在应用调用FindAdvertisedName或AdvertisedName时都会有这个UDP的探测,并且这个过程在底层是一直定时轮休的,这样的一个linux网络机制不难理解。
AllJoynObj::JoinSessionThread::RunJoin -> trans->Connect -> TCPTransport::Connect(new一个TCP Socket ,并connect到远程的Port9955) -> new TCPEndpoint -> DaemonRouter::RegisterEndpoint->
AllJoynObj::AddBusToBusEndpoint->AddVirtualEndpoint
ExchangeNames (注:应该是A Connect B,BConnect C,这里会主动让B和C建立联系)
AllJoynObj::SendAttachSession -> ProxyBusObject::SetB2BEndpoint
建立通信的两个步骤就是Advertised和Join,TCP连接建立之后,会AddVirtualEndpoint,在应用发送数据时,下面的ROUTER会由VirtualEndpoint回找到TCP。
两个应用之间的完整数据路由
Client APP :
ProxyBusObject remoteObj;
remoteObj.MethodCall -> bus->GetInternal().GetRouter().PushMessage
-> ClientRouter::PushMessage -> send (UNIX file system socket)
Client DAEMON:
recv -> RxThread::Run ->DaemonRouter::PushMessage -> SendThroughEndpoint ->VirtualEndpoint::PushMessage -> send(WIFIexample)
Service DAEMON:
recv -> RxThread::Run ->DaemonRouter::PushMessage ->SendThroughEndpoint ->
RemoteEndpoint::PushMessage ->send(UNIX file system socket)
Service APP:
Recv ---do object method----send(UNIX file system socket)
Service DAEMON:
Recv -> RemoteEndpoint::RxThread::Run ->DaemonRouter::PushMessage -> SendThroughEndpoint -> VirtualEndpoint::PushMessage-> send(WIFI example)
Client DAEMON:
recv -> RxThread::Run ->DaemonRouter::PushMessage ->SendThroughEndpoint ->
RemoteEndpoint::PushMessage ->send(UNIX file system socket)
Client APP :
Recv --- the registered ReplyHandler or MethodCall return;