【AllJoyn框架-03】官方示例程序basic簡單剖析

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


發佈了33 篇原創文章 · 獲贊 17 · 訪問量 22萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章