不论是自己编译源码还是从官方下载SDK,在alljoyn_core\samples下的代码很值得研究,有利于熟悉alljoyn框架的各种概念和编程套路。今天我且对basic程序作下简单剖析。
分服务端和客户端。首先看服务端:(我对示例代码做了精简,只保留最核心的API,这样更能抓住主要矛盾又不影响分析)
int main(int argc, char** argv, char** envArg) { signal(SIGINT, SigIntHandler);//创建信号处理,类似于Linux中做法 /* 很多函数调用都返回了这种类型的值,与ER_OK进行比较 我在这里就做了精简,但具体写时是需要进行异常检测的 */ QStatus status = ER_OK; /* 创建BusAttachment对象,myApp为应用程序名,且允许从远程设备接收消息 */ s_msgBus = new BusAttachment("myApp", true); /* 为总线创建接口,以INTERFACE_NAME为接口名,testIntf存储接口描述信息 */ InterfaceDescription* testIntf = NULL; s_msgBus->CreateInterface(INTERFACE_NAME, testIntf); /* 为接口添加方法,cat为方法名,后面为输入、输出参数及列表 */ testIntf->AddMethod("cat", "ss", "s", "inStr1,inStr2,outStr", 0); /* 接口在使用之前要先激活,激活之后不能再被修改 */ testIntf->Activate(); /* 注册总线监听对象,用以监听总线上的事件通知 s_busListener是一个子对象,继承自BusListener,SessionPortListener 重新实现了NameOwnerChanged,AcceptSessionJoiner等虚函数 */ s_msgBus->RegisterBusListener(s_busListener); /* 启动BusAttachment对象中独立的线程,准备运行 它仅仅是启动总线,发送和接收消息要在Connect之后才能开始 BusAttachment线程模型中有一个线程很特殊,它会给注册的监听对象发送callback(回调) 像BusAttachment的Start,Stop,Join方法都可看成是对底层线程方法的映射或叫封装 */ s_msgBus->Start(); /* BasicSampleObject是继承BusObject的子类,它里面实现了方法处理函数Cat */ BasicSampleObject testObj; //为对象添加接口 testObj.AddInterface(*testIntf); //MethodEntry是个结构体 const MethodEntry methodEntries[] = { { exampleIntf->GetMember("cat"), static_cast<MessageReceiver::MethodHandler>(&BasicSampleObject::Cat) } }; //添加方法处理函数,即Cat testObj.AddMethodHandlers(methodEntries, sizeof(methodEntries) / sizeof(methodEntries[0])); // 为总线注册总线对象 s_msgBus->RegisterBusObject(testObj); /* 开始连接到alljoyn router,有一个给定的目的地。如果Connect调用没有给参数,则试图使用一个 bundled router,如果存在的话 可以接收和发送消息了 */ s_msbBus->Connect(); /* 开始发布服务,分三步: 1、请求well-known name 2、创建会话 3、发布服务名 */ const uint32_t flags = DBUS_NAME_FLAG_REPLACE_EXISTING | DBUS_NAME_FLAG_DO_NOT_QUEUE; s_msgBus->RequestName(SERVICE_NAME, flags); const TransportMask SERVICE_TRANSPORT_TYPE = TRANSPORT_ANY; SessionOpts opts(SessionOpts::TRAFFIC_MESSAGES, false, SessionOpts::PROXIMITY_ANY, SERVICE_TRANSPORT_TYPE); SessionPort sp = SERVICE_PORT; // 绑定会话端口 QStatus status = s_msgBus->BindSessionPort(sp, opts, s_busListener); s_msgBus->AdvertiseName(SERVICE_NAME, mask); //等待信号,如果有中断则退出 WaitForSigInt(); delete s_msgBus; s_msgBus = NULL; printf("Basic service exiting with status 0x%04x (%s).\n", status, QCC_StatusText(status)); return (int) status; }
客户端:
int main(int argc, char** argv, char** envArg) { signal(SIGINT, SigIntHandler); QStatus status = ER_OK; g_msgBus = new BusAttachment("myApp", true); g_msgBus->CreateInterface(INTERFACE_NAME, testIntf); testIntf->AddMethod("cat", "ss", "s", "inStr1,inStr2,outStr", 0); testIntf->Activate(); g_msgBus->Start(); g_msgBus->Connect(); static MyBusListener s_busListener; g_msgBus->RegisterBusListener(s_busListener); //前面的代码与service一端差不多 //寻找发布的服务 g_msgBus->FindAdvertisedName(SERVICE_NAME); //等待接入会话 WaitForJoinSessionCompletion(); //创建代理对象进行远程调用 ProxyBusObject remoteObj(*g_msgBus, SERVICE_NAME, SERVICE_PATH, s_sessionId); const InterfaceDescription* alljoynTestIntf = g_msgBus->GetInterface(INTERFACE_NAME); assert(alljoynTestIntf); //给代理对象添加接口 remoteObj.AddInterface(*alljoynTestIntf); Message reply(*g_msgBus); MsgArg inputs[2]; inputs[0].Set("s", "Hello "); inputs[1].Set("s", "World!"); //执行远程调用,reply返回结果 QStatus status = remoteObj.MethodCall(SERVICE_NAME, "cat", inputs, 2, reply, 5000); //输出返回结果 if (ER_OK == status) { printf("'%s.%s' (path='%s') returned '%s'.\n", SERVICE_NAME, "cat", SERVICE_PATH, reply->GetArg(0)->v_string.str); delete g_msgBus; g_msgBus = NULL; printf("Basic client exiting with status 0x%04x (%s).\n", status, QCC_StatusText(status)); return (int) status; }
有一现象得说明一下:当我把MakeMethodCall方法里的代码放进main中时,程序在最后析构的时候会发生异常,不过最后返回值还是会打印出来。若直接调用MakeMethodCall时就正常。
运行结果截图
服务端:
客户端:
由上可知向服务端发送了Hello World,执行了方法调用后返回合成后的字符串。
最后也顺便说一下另外几个工程,其实大致结构与上差不多,具体任务稍有差异
nameChanged_client是修改某个属性名,以signal_service为服务;
signalConsumer_client是订阅了某个属性名信号,一旦名称改变,它会有所反应,也以signal_servie为服务,若要看到现象,nameChanged_client也要参与进来。可参照下图