5.5 客户端开发


  • 5.5.5 C++客户端

    C++客户端支持静态模式,Variant模式。脚本模式通过与Lua库集成实现Lua支持,这个单独介绍。

    C++客户端使用C++11标准实现,最大限度保持和java版本,C#版本一致。C++库在各常用平台上提供支持(g++,ndk,clang),这里以VS2013控制台项目举例。

    • 静态模式

      1. 首先应该创建C++解决方案和应用项目,确定应用项目的源码目录sdir,执行如下生成命令:

      2.

      java -jar <path to limax.jar> xmlgen –c++ –noServiceXML –outputPath sdir example.client.xml
      

      3. 源码目录里面将看见两个新建目录xmlgeninc与xmlgensrc。xmlgeninc无需提交到版本控制系统。

      4. 按照生成目录结构,在项目内建立同样的目录结构,然后使用添加现有项操作手工添加所有生成的.cpp文件,以后的新生成的.cpp文件也必须手工添加。为了编辑器查看方便建议.h也添加进来。

      5. 将C++版本的limax项目添加到解决方案中。

      6. 为应用项目添加引用,引用到limax项目。

      7. 编辑项目属性,将limax项目的include目录作为附加包含目录。

      主函数代码:


      #include "stdafx.h"
      #include <iostream>
      #include "limax.h"
      #include "xmlgeninc/xmlgen.h"
      using namespace limax;
      
      class MyApp : public EndpointListener
      {
          const int providerId = 100;
      public:
          MyApp()
          {
              Endpoint::openEngine();
              auto config = Endpoint::createEndpointConfigBuilder(
                      "127.0.0.1", 10000, LoginConfig::plainLogin("testabc", "123456", "test"))
                  ->staticViewCreatorManagers({ example::getShareViewCreatorManager(providerId) })
                  ->endpointState({ example::getExampleClientStateClient(providerId) })
                  ->build();
              Endpoint::start(config, this);
          }
          ~MyApp(){
              std::mutex mutex;
              std::condition_variable_any cond;
              std::lock_guard<std::mutex> l(mutex);
              Endpoint::closeEngine([&](){
                  std::lock_guard<std::mutex> l(mutex);
                  cond.notify_one();
              });
              cond.wait(mutex);
          }
          void run()  { Sleep(2000); }
          void onManagerInitialized(EndpointManager*,EndpointConfig*) { std::cout << "onManagerInitialized" << std::endl; }
          void onManagerUninitialized(EndpointManager*) { std::cout << "onManagerUninitialized" << std::endl; }
          void onTransportAdded(Transport*) {
              std::cout << "onTransportAdded" << std::endl;
              View* mysessionview = example::ExampleClient::share::MySessionView::getInstance();
              mysessionview->registerListener([](const ViewChangedEvent &e){std::cout << e.toString() << std::endl; });
          }
          void onTransportRemoved(Transport*){ std::cout << "onTransportRemoved" << std::endl; }
          void onAbort(Transport*) { std::cout << "onAbort" << std::endl; }
          void onSocketConnected() { std::cout << "onSocketConnected" << std::endl; }
          void onKeyExchangeDone() { std::cout << "onKeyExchangeDone" << std::endl; }
          void onKeepAlived(int ping) { std::cout << "onKeepAlived " << ping << std::endl; }
          void onErrorOccured(int errorsource, int errorvalue, const std::string& info) { std::cout << "onErrorOccured " << errorsource << " " << errorvalue << " " << info << std::endl; }
          void destroy() {}
      };
      
      int _tmain(int argc, _TCHAR* argv[])
      {
          MyApp().run();
          return 0;
      }    
      

      与java版本C#版本比起来除语言特性外无太大差异。onErrorOccured稍有差别,当source为SOURCE_ENDPOINT时,code对应那些SYSTEM类型错误,info报告了具体错误内容,source/code的定义参见endpoint.h头文件。另外MyApp类中多了一个方法destroy。具体原因下面介绍。

      share.MyTemporaryView.cpp:


      #include "share.MyTemporaryView.h"
      #include "../xmlgeninc/views/share.MySessionView.h"
      
      #include <iostream>
      using namespace limax;
      namespace example { 
      namespace ExampleClient { 
      namespace share { 
      
          void MyTemporaryView::onOpen(const std::vector<int64_t>& sessionids) {
              std::cout << toString() << " onOpen";
              for (auto &l : sessionids)
                  std::cout << " " << l;
              std::cout << std::endl;
              registerListener([](const ViewChangedEvent &e){std::cout << e.toString() << std::endl; });
              MySessionView::getInstance()->control(99999);
          }
          void MyTemporaryView::onAttach(int64_t sessionid) {}
          void MyTemporaryView::onDetach(int64_t sessionid, int reason)
          {
              if (reason >= 0)
              {
                  //Application Reason
              }
              else
              {
                  //Connection abort Reason
              }
          }
          void MyTemporaryView::onClose() {
              std::cout <<  toString() << " onClose " << std::endl;
          }
      } 
      } 
      }
      

      这个看起来差异也不大,注意一下,因为使用了MySessionView,所以需要的头文件share.MySessionView.h必须自己手工include。另外,同一目录下生成的头文件share.MyTemporaryView.h中,可以添加需要的成员变量,成员方法。


      运行结果:

      onManagerInitialized

      onSocketConnected

      onKeyExchangeDone

      onTransportAdded

      [class = example.ExampleClient.share.MyTemporaryView ProviderId = 100 classindex = 2 instanceindex = 78] onOpen 61440

      [class = example.ExampleClient.share.MyTemporaryView ProviderId = 100 classindex = 2 instanceindex = 78] 61440 _var0 00A3BCBC NEW

      onKeepAlived 15

      [class = example.ExampleClient.share.MySessionView ProviderId = 100 classindex = 1] 61440 var0 0088585C NEW

      [class = example.ExampleClient.share.MySessionView ProviderId = 100 classindex = 1] 61440 var0 0088585C REPLACE

      [class = example.ExampleClient.share.MyTemporaryView ProviderId = 100 classindex = 2 instanceindex = 78] 61440 _var0 00A3BCBC REPLACE

      onTransportRemoved

      [class = example.ExampleClient.share.MyTemporaryView ProviderId = 100 classindex = 2 instanceindex = 78] onClose

      onManagerUninitialized

      这个结果看起来与前面各版本均一致,只不过View的值被显示为了一个地址了,这是因为消息通告ViewChangedEvent的定义中,value的类型只能是void*。


    • Variant模式

      修改构造函数,创建Variant模式下的配置


      auto config = Endpoint::createEndpointConfigBuilder(
              "127.0.0.1", 10000, LoginConfig::plainLogin("testabc", "123456", "test"))
          ->variantProviderIds({ providerId })
          ->build();    
      

      修改onTransportAdded方法:


      class MyTemporaryViewHandler : public TemporaryViewHandler
      {
              VariantView* mySessionView;
          public:
              MyTemporaryViewHandler(VariantView* v) : mySessionView(v) {}
              virtual void onOpen(VariantView* view, const std::vector<int64_t>& sessionids){
                  std::cout << view->toString() << " onOpen";
                  for (auto &l : sessionids)
                      std::cout << " " << l;
                  std::cout << std::endl;
                  view->registerListener([](const VariantViewChangedEvent &e){std::cout << e.toString() << std::endl; });
                  Variant param = Variant::createStruct();
                  param.setValue("var0", 99999);
                  mySessionView->sendControl("control", param);
              }
              virtual void onClose(VariantView* view){
                  std::cout << view->toString() << " onClose " << std::endl;
              }
              virtual void onAttach(VariantView* view, int64_t sessionid) {}
              virtual void onDetach(VariantView* view, int64_t sessionid, int reason) {}
              virtual void destroy() { delete this; }
      };
      
      void onTransportAdded(Transport*transport) {
          std::cout << "onTransportAdded" << std::endl;
          VariantManager* manager = VariantManager::getInstance(transport->getManager(), providerId);
          VariantView* mySessionView = manager->getSessionOrGlobalView("share.MySessionView");
          mySessionView->registerListener([](const VariantViewChangedEvent &e){std::cout << e.toString() << std::endl; });
          manager->setTemporaryViewHandler("share.MyTemporaryView", new MyTemporaryViewHandler(mySessionView));
      } 
      

      这段代码看起来和C#版本非常类似,除了语言特性差异。另外MyTemporaryViewHandler类中多了一个方法destroy。具体原因下面介绍。

      运行结果:

      onManagerInitialized

      onSocketConnected

      onKeyExchangeDone

      onTransportAdded

      [view = share.MyTemporaryView ProviderId = 100 classindex = 2 instanceindex = 79] onOpen 61440

      [view = share.MyTemporaryView ProviderId = 100 classindex = 2 instanceindex = 79] 61440 _var0 Hello 61440 NEW

      onKeepAlived 16

      [view = share.MySessionView ProviderId = 100 classindex = 1] 61440 var0 Hello 61440 NEW

      [view = share.MySessionView ProviderId = 100 classindex = 1] 61440 var0 99999 REPLACE

      [view = share.MyTemporaryView ProviderId = 100 classindex = 2 instanceindex = 79] 61440 _var0 99999 REPLACE

      onTransportRemoved

      [view = share.MyTemporaryView ProviderId = 100 classindex = 2 instanceindex = 79] onClose

      onManagerUninitialized

      这里的结果就能看到实际的value了,因为Variant知道一些类型信息。


    • 各种注意事项

      1. 主函数代码必须包含头文件limax.h,使用静态模式需要额外包含生成的头文件xmlgeninc/xmlgen.h。修改生成代码时, 如果需要使用别的生成代码, 需要哪些包含哪些。参见前面对share.MyTemporaryView.cpp的修改。

      2. 为了简单起见,limax库代码只使用limax名字空间,不再作进一步的划分,所以using namespace limax;

      3. 创建配置时,staticViewCreatorManagers,endpointState,variantProviderIds的参数使用C++11特性的初始化列表作为参数,可以支持多个。同C#一样,同样提供runOnUiThread与uiThreadSchedule两个辅助方法方便使用,->executor([](Runnable r){ runOnUiThread(r); })。

      4. 由库提供出来的各种指针EndpointManager*,Trasnport*,以及各种形式的View*,用户可以直接使用不允许delete。EndpointManager 的寿命到onManagerUninitialized为止;Transport以及全局View,会话View的寿命到onTransportRemoved为止;临时View的寿命到临时View自身onClose为止,如果一定需要持有这些指针,必须严格注意寿命问题,尤其是通过lambda表达式的闭包引用更要小心。

      5. 需要应用提供对象的情况下,对象应该由用户自己创建出来提供指针交由库使用,并且提供自己的destroy方法,前面的MyApp::destroy 和 MyTemporaryViewHandler::destroy就是典型情况。这是由于库内部使用的new/delete与应用本身的new/delete可能不配对。

      6. 如果使用Objective-C++,生成静态模式代码时使用-oc参数代替-c++参数,确保生成需要的.mm文件。

上一页 下一页