不論是自己編譯源碼還是從官方下載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也要參與進來。可參照下圖