深入浅出Alljoyn——实例分析之远程调用(Method)篇
深入浅出就是很深入的学习了很久,还是只学了毛皮,呵呵!
服务端完整代码:
1 #include <qcc/platform.h> 2 3 #include <assert.h> 4 #include <signal.h> 5 #include <stdio.h> 6 #include <vector> 7 8 #include <qcc/String.h> 9 10 #include <alljoyn/BusAttachment.h> 11 #include <alljoyn/DBusStd.h> 12 #include <alljoyn/AllJoynStd.h> 13 #include <alljoyn/BusObject.h> 14 #include <alljoyn/MsgArg.h> 15 #include <alljoyn/version.h> 16 17 #include <alljoyn/Status.h> 18 19 using namespace std; 20 using namespace qcc; 21 using namespace ajn; 22 23 /*constants*/ 24 static const char* INTERFACE_NAME = "org.alljoyn.Bus.sample"; 25 static const char* SERVICE_NAME = "org.alljoyn.Bus.sample"; 26 static const char* SERVICE_PATH = "/sample"; 27 static const SessionPort SERVICE_PORT = 25; 28 29 static volatile sig_atomic_t s_interrupt = false; 30 31 static void SigIntHandler(int sig) 32 { 33 s_interrupt = true; 34 } 35 36 class BasicSampleObject : public BusObject { 37 public: 38 BasicSampleObject(BusAttachment& bus, const char* path) : 39 BusObject(path) 40 { 41 /** Add the test interface to this object */ 42 const InterfaceDescription* exampleIntf = bus.GetInterface(INTERFACE_NAME); 43 assert(exampleIntf); 44 AddInterface(*exampleIntf); 45 46 /** Register the method handlers with the object */ 47 const MethodEntry methodEntries[] = { 48 { exampleIntf->GetMember("cat"), static_cast<MessageReceiver::MethodHandler>(&BasicSampleObject::Cat) } 49 }; 50 QStatus status = AddMethodHandlers(methodEntries, sizeof(methodEntries) / sizeof(methodEntries[0])); 51 if (ER_OK != status) { 52 printf("Failed to register method handlers for BasicSampleObject.\n"); 53 } 54 } 55 56 void ObjectRegistered() 57 { 58 BusObject::ObjectRegistered(); 59 printf("ObjectRegistered has been called.\n"); 60 } 61 62 63 void Cat(const InterfaceDescription::Member* member, Message& msg) 64 { 65 /* Concatenate the two input strings and reply with the result. */ 66 qcc::String inStr1 = msg->GetArg(0)->v_string.str; 67 qcc::String inStr2 = msg->GetArg(1)->v_string.str; 68 qcc::String outStr = inStr1 + inStr2; 69 70 MsgArg outArg("s", outStr.c_str()); 71 QStatus status = MethodReply(msg, &outArg, 1); 72 if (ER_OK != status) { 73 printf("Ping: Error sending reply.\n"); 74 } 75 } 76 }; 77 78 class MyBusListener : public BusListener, public SessionPortListener { 79 void NameOwnerChanged(const char* busName, const char* previousOwner, const char* newOwner) 80 { 81 if (newOwner && (0 == strcmp(busName, SERVICE_NAME))) { 82 printf("NameOwnerChanged: name=%s, oldOwner=%s, newOwner=%s.\n", 83 busName, 84 previousOwner ? previousOwner : "<none>", 85 newOwner ? newOwner : "<none>"); 86 } 87 } 88 bool AcceptSessionJoiner(SessionPort sessionPort, const char* joiner, const SessionOpts& opts) 89 { 90 if (sessionPort != SERVICE_PORT) { 91 printf("Rejecting join attempt on unexpected session port %d.\n", sessionPort); 92 return false; 93 } 94 printf("Accepting join session request from %s (opts.proximity=%x, opts.traffic=%x, opts.transports=%x).\n", 95 joiner, opts.proximity, opts.traffic, opts.transports); 96 return true; 97 } 98 }; 99 100 /** The bus listener object. */ 101 static MyBusListener s_busListener; 102 103 /** Top level message bus object. */ 104 static BusAttachment* s_msgBus = NULL; 105 106 /** Create the interface, report the result to stdout, and return the result status. */ 107 QStatus CreateInterface(void) 108 { 109 /* Add org.alljoyn.Bus.method_sample interface */ 110 InterfaceDescription* testIntf = NULL; 111 QStatus status = s_msgBus->CreateInterface(INTERFACE_NAME, testIntf); 112 113 if (status == ER_OK) { 114 printf("Interface created.\n"); 115 testIntf->AddMethod("cat", "ss", "s", "inStr1,inStr2,outStr", 0); 116 testIntf->Activate(); 117 } else { 118 printf("Failed to create interface '%s'.\n", INTERFACE_NAME); 119 } 120 121 return status; 122 } 123 124 /** Register the bus object and connect, report the result to stdout, and return the status code. */ 125 QStatus RegisterBusObject(BasicSampleObject* obj) 126 { 127 QStatus status = s_msgBus->RegisterBusObject(*obj); 128 129 if (ER_OK == status) { 130 printf("RegisterBusObject succeeded.\n"); 131 } else { 132 printf("RegisterBusObject failed (%s).\n", QCC_StatusText(status)); 133 } 134 135 return status; 136 } 137 138 /** Connect, report the result to stdout, and return the status code. */ 139 QStatus ConnectBusAttachment(void) 140 { 141 QStatus status = s_msgBus->Connect(); 142 143 if (ER_OK == status) { 144 printf("Connect to '%s' succeeded.\n", s_msgBus->GetConnectSpec().c_str()); 145 } else { 146 printf("Failed to connect to '%s' (%s).\n", s_msgBus->GetConnectSpec().c_str(), QCC_StatusText(status)); 147 } 148 149 return status; 150 } 151 152 /** Start the message bus, report the result to stdout, and return the status code. */ 153 QStatus StartMessageBus(void) 154 { 155 QStatus status = s_msgBus->Start(); 156 157 if (ER_OK == status) { 158 printf("BusAttachment started.\n"); 159 } else { 160 printf("Start of BusAttachment failed (%s).\n", QCC_StatusText(status)); 161 } 162 163 return status; 164 } 165 166 /** Create the session, report the result to stdout, and return the status code. */ 167 QStatus CreateSession(TransportMask mask) 168 { 169 SessionOpts opts(SessionOpts::TRAFFIC_MESSAGES, false, SessionOpts::PROXIMITY_ANY, mask); 170 SessionPort sp = SERVICE_PORT; 171 QStatus status = s_msgBus->BindSessionPort(sp, opts, s_busListener); 172 173 if (ER_OK == status) { 174 printf("BindSessionPort succeeded.\n"); 175 } else { 176 printf("BindSessionPort failed (%s).\n", QCC_StatusText(status)); 177 } 178 179 return status; 180 } 181 182 /** Advertise the service name, report the result to stdout, and return the status code. */ 183 QStatus AdvertiseName(TransportMask mask) 184 { 185 QStatus status = s_msgBus->AdvertiseName(SERVICE_NAME, mask); 186 187 if (ER_OK == status) { 188 printf("Advertisement of the service name '%s' succeeded.\n", SERVICE_NAME); 189 } else { 190 printf("Failed to advertise name '%s' (%s).\n", SERVICE_NAME, QCC_StatusText(status)); 191 } 192 193 return status; 194 } 195 196 /** Request the service name, report the result to stdout, and return the status code. */ 197 QStatus RequestName(void) 198 { 199 const uint32_t flags = DBUS_NAME_FLAG_REPLACE_EXISTING | DBUS_NAME_FLAG_DO_NOT_QUEUE; 200 QStatus status = s_msgBus->RequestName(SERVICE_NAME, flags); 201 202 if (ER_OK == status) { 203 printf("RequestName('%s') succeeded.\n", SERVICE_NAME); 204 } else { 205 printf("RequestName('%s') failed (status=%s).\n", SERVICE_NAME, QCC_StatusText(status)); 206 } 207 208 return status; 209 } 210 211 /** Wait for SIGINT before continuing. */ 212 void WaitForSigInt(void) 213 { 214 while (s_interrupt == false) { 215 #ifdef _WIN32 216 Sleep(100); 217 #else 218 usleep(100 * 1000); 219 #endif 220 } 221 } 222 223 /** Main entry point */ 224 int main(int argc, char** argv, char** envArg) 225 { 226 printf("AllJoyn Library version: %s.\n", ajn::GetVersion()); 227 printf("AllJoyn Library build info: %s.\n", ajn::GetBuildInfo()); 228 229 /* Install SIGINT handler */ 230 signal(SIGINT, SigIntHandler); 231 232 QStatus status = ER_OK; 233 234 /* Create message bus */ 235 s_msgBus = new BusAttachment("myApp", true); 236 237 if (!s_msgBus) { 238 status = ER_OUT_OF_MEMORY; 239 } 240 241 if (ER_OK == status) { 242 status = CreateInterface(); 243 } 244 245 if (ER_OK == status) { 246 s_msgBus->RegisterBusListener(s_busListener); 247 } 248 249 if (ER_OK == status) { 250 status = StartMessageBus(); 251 } 252 253 BasicSampleObject testObj(*s_msgBus, SERVICE_PATH); 254 255 if (ER_OK == status) { 256 status = RegisterBusObject(&testObj); 257 } 258 259 if (ER_OK == status) { 260 status = ConnectBusAttachment(); 261 } 262 263 /* 264 * Advertise this service on the bus. 265 * There are three steps to advertising this service on the bus. 266 * 1) Request a well-known name that will be used by the client to discover 267 * this service. 268 * 2) Create a session. 269 * 3) Advertise the well-known name. 270 */ 271 if (ER_OK == status) { 272 status = RequestName(); 273 } 274 275 const TransportMask SERVICE_TRANSPORT_TYPE = TRANSPORT_ANY; 276 277 if (ER_OK == status) { 278 status = CreateSession(SERVICE_TRANSPORT_TYPE); 279 } 280 281 if (ER_OK == status) { 282 status = AdvertiseName(SERVICE_TRANSPORT_TYPE); 283 } 284 285 /* Perform the service asynchronously until the user signals for an exit. */ 286 if (ER_OK == status) { 287 WaitForSigInt(); 288 } 289 290 /* Clean up msg bus */ 291 delete s_msgBus; 292 s_msgBus = NULL; 293 294 printf("Basic service exiting with status 0x%04x (%s).\n", status, QCC_StatusText(status)); 295 296 return (int) status; 297 }
服务端主流程解析:
int main(int argc, char** argv, char** envArg) { /* 注册系统信号回调函数*/ signal(SIGINT, SigIntHandler); /* 创建Bus连接器 */ s_msgBus = new BusAttachment("myApp", true); /* 创建object 的接口*/ status = CreateInterface(); /*绑定总线监听器*/ s_msgBus->RegisterBusListener(s_busListener); /*开启总线*/ status = StartMessageBus(); /*实例化对象*/ BasicSampleObject testObj(*s_msgBus, SERVICE_PATH); /*总线附件上注册对象*/ status = RegisterBusObject(&testObj); /*连接到总线上*/ status = ConnectToDaemon(); /*请求一个服务名即wellknow name*/ status = RequestName(); const TransportMask SERVICE_TRANSPORT_TYPE = TRANSPORT_ANY; /*创建一个会话*/ status = CreateSession(SERVICE_TRANSPORT_TYPE); /*在总线上广播服务名*/ status = AdvertiseName(SERVICE_TRANSPORT_TYPE); /* 等待用户终止 */ WaitForSigInt(); /* 释放资源 */ delete s_msgBus; s_msgBus = NULL; return (int) status; }
客户端完整代码:
1 #include <qcc/platform.h> 2 3 #include <assert.h> 4 #include <signal.h> 5 #include <stdio.h> 6 #include <vector> 7 8 #include <qcc/String.h> 9 10 #include <alljoyn/BusAttachment.h> 11 #include <alljoyn/version.h> 12 #include <alljoyn/AllJoynStd.h> 13 #include <alljoyn/Status.h> 14 15 using namespace std; 16 using namespace qcc; 17 using namespace ajn; 18 19 /** Static top level message bus object */ 20 static BusAttachment* g_msgBus = NULL; 21 22 /*constants*/ 23 static const char* INTERFACE_NAME = "org.alljoyn.Bus.sample"; 24 static const char* SERVICE_NAME = "org.alljoyn.Bus.sample"; 25 static const char* SERVICE_PATH = "/sample"; 26 static const SessionPort SERVICE_PORT = 25; 27 28 static bool s_joinComplete = false; 29 static SessionId s_sessionId = 0; 30 31 static volatile sig_atomic_t s_interrupt = false; 32 33 static void SigIntHandler(int sig) 34 { 35 s_interrupt = true; 36 } 37 38 /** AllJoynListener receives discovery events from AllJoyn */ 39 class MyBusListener : public BusListener, public SessionListener { 40 public: 41 void FoundAdvertisedName(const char* name, TransportMask transport, const char* namePrefix) 42 { 43 if (0 == strcmp(name, SERVICE_NAME)) { 44 printf("FoundAdvertisedName(name='%s', prefix='%s')\n", name, namePrefix); 45 46 /* We found a remote bus that is advertising basic service's well-known name so connect to it. */ 47 /* Since we are in a callback we must enable concurrent callbacks before calling a synchronous method. */ 48 g_msgBus->EnableConcurrentCallbacks(); 49 SessionOpts opts(SessionOpts::TRAFFIC_MESSAGES, false, SessionOpts::PROXIMITY_ANY, TRANSPORT_ANY); 50 QStatus status = g_msgBus->JoinSession(name, SERVICE_PORT, this, s_sessionId, opts); 51 if (ER_OK == status) { 52 printf("JoinSession SUCCESS (Session id=%d).\n", s_sessionId); 53 } else { 54 printf("JoinSession failed (status=%s).\n", QCC_StatusText(status)); 55 } 56 } 57 s_joinComplete = true; 58 } 59 60 void NameOwnerChanged(const char* busName, const char* previousOwner, const char* newOwner) 61 { 62 if (newOwner && (0 == strcmp(busName, SERVICE_NAME))) { 63 printf("NameOwnerChanged: name='%s', oldOwner='%s', newOwner='%s'.\n", 64 busName, 65 previousOwner ? previousOwner : "<none>", 66 newOwner ? newOwner : "<none>"); 67 } 68 } 69 }; 70 71 /** Create the interface, report the result to stdout, and return the result status. */ 72 QStatus CreateInterface(void) 73 { 74 /* Add org.alljoyn.Bus.method_sample interface */ 75 InterfaceDescription* testIntf = NULL; 76 QStatus status = g_msgBus->CreateInterface(INTERFACE_NAME, testIntf); 77 78 if (status == ER_OK) { 79 printf("Interface '%s' created.\n", INTERFACE_NAME); 80 testIntf->AddMethod("cat", "ss", "s", "inStr1,inStr2,outStr", 0); 81 testIntf->Activate(); 82 } else { 83 printf("Failed to create interface '%s'.\n", INTERFACE_NAME); 84 } 85 86 return status; 87 } 88 89 /** Start the message bus, report the result to stdout, and return the result status. */ 90 QStatus StartMessageBus(void) 91 { 92 QStatus status = g_msgBus->Start(); 93 94 if (ER_OK == status) { 95 printf("BusAttachment started.\n"); 96 } else { 97 printf("BusAttachment::Start failed.\n"); 98 } 99 100 return status; 101 } 102 103 /** Handle the connection to the bus, report the result to stdout, and return the result status. */ 104 QStatus ConnectToBus(void) 105 { 106 QStatus status = g_msgBus->Connect(); 107 108 if (ER_OK == status) { 109 printf("BusAttachment connected to '%s'.\n", g_msgBus->GetConnectSpec().c_str()); 110 } else { 111 printf("BusAttachment::Connect('%s') failed.\n", g_msgBus->GetConnectSpec().c_str()); 112 } 113 114 return status; 115 } 116 117 /** Register a bus listener in order to get discovery indications and report the event to stdout. */ 118 void RegisterBusListener(void) 119 { 120 /* Static bus listener */ 121 static MyBusListener s_busListener; 122 123 g_msgBus->RegisterBusListener(s_busListener); 124 printf("BusListener Registered.\n"); 125 } 126 127 /** Begin discovery on the well-known name of the service to be called, report the result to 128 stdout, and return the result status. */ 129 QStatus FindAdvertisedName(void) 130 { 131 /* Begin discovery on the well-known name of the service to be called */ 132 QStatus status = g_msgBus->FindAdvertisedName(SERVICE_NAME); 133 134 if (status == ER_OK) { 135 printf("org.alljoyn.Bus.FindAdvertisedName ('%s') succeeded.\n", SERVICE_NAME); 136 } else { 137 printf("org.alljoyn.Bus.FindAdvertisedName ('%s') failed (%s).\n", SERVICE_NAME, QCC_StatusText(status)); 138 } 139 140 return status; 141 } 142 143 /** Wait for join session to complete, report the event to stdout, and return the result status. */ 144 QStatus WaitForJoinSessionCompletion(void) 145 { 146 unsigned int count = 0; 147 148 while (!s_joinComplete && !s_interrupt) { 149 if (0 == (count++ % 10)) { 150 printf("Waited %u seconds for JoinSession completion.\n", count / 10); 151 } 152 153 #ifdef _WIN32 154 Sleep(100); 155 #else 156 usleep(100 * 1000); 157 #endif 158 } 159 160 return s_joinComplete && !s_interrupt ? ER_OK : ER_ALLJOYN_JOINSESSION_REPLY_CONNECT_FAILED; 161 } 162 163 /** Do a method call, report the result to stdout, and return the result status. */ 164 QStatus MakeMethodCall(void) 165 { 166 ProxyBusObject remoteObj(*g_msgBus, SERVICE_NAME, SERVICE_PATH, s_sessionId); 167 const InterfaceDescription* alljoynTestIntf = g_msgBus->GetInterface(INTERFACE_NAME); 168 169 assert(alljoynTestIntf); 170 remoteObj.AddInterface(*alljoynTestIntf); 171 172 Message reply(*g_msgBus); 173 MsgArg inputs[2]; 174 175 inputs[0].Set("s", "Hello "); 176 inputs[1].Set("s", "World!"); 177 178 QStatus status = remoteObj.MethodCall(SERVICE_NAME, "cat", inputs, 2, reply, 5000); 179 180 if (ER_OK == status) { 181 printf("'%s.%s' (path='%s') returned '%s'.\n", SERVICE_NAME, "cat", 182 SERVICE_PATH, reply->GetArg(0)->v_string.str); 183 } else { 184 printf("MethodCall on '%s.%s' failed.", SERVICE_NAME, "cat"); 185 } 186 187 return status; 188 } 189 190 /** Main entry point */ 191 int main(int argc, char** argv, char** envArg) 192 { 193 printf("AllJoyn Library version: %s.\n", ajn::GetVersion()); 194 printf("AllJoyn Library build info: %s.\n", ajn::GetBuildInfo()); 195 196 /* Install SIGINT handler. */ 197 signal(SIGINT, SigIntHandler); 198 199 QStatus status = ER_OK; 200 201 /* Create message bus. */ 202 g_msgBus = new BusAttachment("myApp", true); 203 204 /* This test for NULL is only required if new() behavior is to return NULL 205 * instead of throwing an exception upon an out of memory failure. 206 */ 207 if (!g_msgBus) { 208 status = ER_OUT_OF_MEMORY; 209 } 210 211 if (ER_OK == status) { 212 status = CreateInterface(); 213 } 214 215 if (ER_OK == status) { 216 status = StartMessageBus(); 217 } 218 219 if (ER_OK == status) { 220 status = ConnectToBus(); 221 } 222 223 if (ER_OK == status) { 224 RegisterBusListener(); 225 status = FindAdvertisedName(); 226 } 227 228 if (ER_OK == status) { 229 status = WaitForJoinSessionCompletion(); 230 } 231 232 if (ER_OK == status) { 233 status = MakeMethodCall(); 234 } 235 236 /* Deallocate bus */ 237 delete g_msgBus; 238 g_msgBus = NULL; 239 240 printf("Basic client exiting with status 0x%04x (%s).\n", status, QCC_StatusText(status)); 241 242 return (int) status; 243 }
客户端主流程解析:
int main(int argc, char** argv, char** envArg) { printf("AllJoyn Library version: %s.\n", ajn::GetVersion()); printf("AllJoyn Library build info: %s.\n", ajn::GetBuildInfo()); /* 注册系统信号回调函数*/ signal(SIGINT, SigIntHandler); QStatus status = ER_OK; /* 创建Bus连接器 */ g_msgBus = new BusAttachment("myApp", true); /* This test for NULL is only required if new() behavior is to return NULL * instead of throwing an exception upon an out of memory failure. */ if (!g_msgBus) { status = ER_OUT_OF_MEMORY; } /* 创建object 的接口*/ if (ER_OK == status) { status = CreateInterface(); } /*开启总线*/ if (ER_OK == status) { status = StartMessageBus(); } /*连接到总线上*/ if (ER_OK == status) { status = ConnectToBus(); } if (ER_OK == status) { RegisterBusListener();/*绑定总线监听器*/ status = FindAdvertisedName();/*在总线上发现服务名*/ } if (ER_OK == status) { status = WaitForJoinSessionCompletion(); } /*远程调用方法*/ if (ER_OK == status) { status = MakeMethodCall(); } /* 释放资源 */ delete g_msgBus; g_msgBus = NULL; printf("Basic client exiting with status 0x%04x (%s).\n", status, QCC_StatusText(status)); return (int) status; }
通信过程中比较重要的几点:
1、 接口由bus连接器创建和保存所以创建的实例
2、 对象具体实现接口的属性(方法,属性和信号),与具体接口绑定,作为对象对外的接口。(可以含有多个接口)
3、连接总线s_msgBus->Connect()是通过dbus方式实现的,利用了dbus中现存的方法和消息。
用下面的图可能表达的更清楚:
通过Bus建立好连接后,服务端的object提供接口比如说方法, 那么客户端的object只需产生一个object的代理,即可调用服务端的object提供接口,是不是比较好玩。
服务端方法调用的代码:
服务端:通过接受客户端提供的参数,并通过MethodReply()给客户端返回结果 void Cat(const InterfaceDescription::Member* member, Message& msg) { /* Concatenate the two input strings and reply with the result. */ qcc::String inStr1 = msg->GetArg(0)->v_string.str; qcc::String inStr2 = msg->GetArg(1)->v_string.str; qcc::String outStr = inStr1 + inStr2; MsgArg outArg("s", outStr.c_str()); QStatus status = MethodReply(msg, &outArg, 1); if (ER_OK != status) { printf("Ping: Error sending reply.\n"); } }
客户端方法调用的代码:
QStatus status = remoteObj.MethodCall(SERVICE_NAME, "cat", inputs, 2, reply, 5000);
说了这么多,这个代码的作用是干嘛呢,就是服务端提供一个连接字符串(str1+str2)的方法,并将结果返回给客户端。当然这只是一个很简单sample,如果你将你家电视定义了换台方法的话,用手机就可以通过调用这个方法进行控制了,当然前提是你的电视和手机在一个网内!
一个人学习有时挺没意思的,那就加入q群49073007一起交流讨论吧。