zoukankan      html  css  js  c++  java
  • AllJoyn Bundled Daemon 使用方式研究

    关于AllJoyn不多做介绍,请看官网:www.alljoyn.org/

    0. 问题来源:

    应用程序要使用AllJoyn必须启动deamon

    目前有两种方式:

    • 使用standalone形式,单独启动alljoyn-daemon进程。
    • 使用bundled daemon,应用程序在连接AllJoyn时自己启动该deamon

    AllJoyn的开发者解释如下:https://www.alljoyn.org/forums/developers/building-embedded-linux-system

    The decision to use standalone or bundled daemon is made runtime but there is also an additional component that specifies whether an AllJoyn library supports bundled daemon at all. This the BD variable that we use when we build AllJoyn using scons.

     More specifically, the check to choose which daemon is always made in the same manner irrespective of whether we have a standalone daemon or bundled daemon. The only thing BD signifies during build is whether the library will actually include code to run bundled daemon.

     An AllJoyn application will always see is there is a standalone daemon running. If yes it will connect to that daemon else it will try and use the bundled daemon which may or may not be a part of the library depending on whether you said BD=on or off.

    bundled daemon式不需要单独启动进程,比较方便。因此就来研究如何使用该种方式。

    1. 从入口看起:

    BusAttachment::Connect()
    {
    #ifdef _WIN32
        const char* connectArgs = "tcp:addr=127.0.0.1,port=9956";
    #else
        const char* connectArgs = "unix:abstract=alljoyn";
    #endif
        return Connect(connectArgs);
    }
    

    很明显,针对windows使用tcp transport,其他平台使用“unix:abstract=alljoyn“transport

    然后调用内部函数BusAttachment::Connect(const char* connectSpec)进行连接。

    2. BusAttachment::Connect(const char* connectSpec)函数,重点如下:

    ...
     this->connectSpec = connectSpec;
            status = TryConnect(connectSpec);
            /*
             * Try using the null transport to connect to a bundled daemon if there is one
             */
            if (status != ER_OK && !isDaemon) {
                        printf("TryConnect() failed.
    ");
                        
                qcc::String bundledConnectSpec = "null:";
                if (bundledConnectSpec != connectSpec) {
                    status = TryConnect(bundledConnectSpec.c_str());
                    if (ER_OK == status) {
                        this->connectSpec = bundledConnectSpec;
                    }
                }
            }
    ...
    

    首先尝试连接指定的"unix:abstract=alljoyn";

    如果连接失败就尝试连接名为”null:”的transport(对应的类是NullTransport )------该transport 即使用bundled daemon 

    3. 再看BusAttachment::TryConnect(const char* connectSpec)函数,重点如下:

    ...
    /* Get or create transport for connection */
        Transport* trans = busInternal->transportList.GetTransport(connectSpec);
        if (trans) {
            SessionOpts emptyOpts;
            status = trans->Connect(connectSpec, emptyOpts, tempEp);
             ...
         }
    ...
    

    从busInternal中获取指定的transport,再进行连接。
    那么busInternal是如何存储transport的呢?

    4. 看busInternal的初始化过程:

    BusAttachment::Internal::Internal(const char* appName,
                                      BusAttachment& bus,
                                      TransportFactoryContainer& factories,
                                      Router* router,
                                      bool allowRemoteMessages,
                                      const char* listenAddresses,
                                      uint32_t concurrency) :
        application(appName ? appName : "unknown"),
        bus(bus),
        listenersLock(),
        listeners(),
        m_ioDispatch("iodisp", 16),
        transportList(bus, factories, &m_ioDispatch, concurrency),
        keyStore(application),
        authManager(keyStore),
        globalGuid(qcc::GUID128()),
        msgSerial(1),
        router(router ? router : new ClientRouter),
        localEndpoint(transportList.GetLocalTransport()->GetLocalEndpoint()),
        allowRemoteMessages(allowRemoteMessages),
        listenAddresses(listenAddresses ? listenAddresses : ""),
        stopLock(),
        stopCount(0)
    {
      ...
    }
    

    可以看到,这里是通过TransportFactoryContainer(一组transport factory)对象来初始化所支持的transport list。

    5.

    QStatus TransportList::Start(const String& transportSpecs)
    {
    ...
    	for (uint32_t i = 0; i < m_factories.Size(); ++i) {
                            		TransportFactoryBase* factory = m_factories.Get(i);
                            		if (factory->GetType() == ttype && factory->IsDefault() == false) {
                                		transportList.push_back(factory->Create(bus));
                            		}
                        	}
    ...
    }
    

    可以看到,这里根据每个transport factory来创建对应的transport,并添加到transportList.中。

    所以问题的关键在于:TransportFactoryContainer是如何初始化的呢(由transport factory 来决定支持的transport list)?

    6. 再回过头来看BusAttachment的构造函数:

    BusAttachment::BusAttachment(const char* applicationName, bool allowRemoteMessages, uint32_t concurrency) :
        isStarted(false),
        isStopping(false),
        concurrency(concurrency),
        busInternal(new Internal(applicationName, *this, clientTransportsContainer, NULL, allowRemoteMessages, NULL, concurrency)),
        joinObj(this)
    {
    ...
    }
    

    原来是BusAttachment在构造对象时初始化了Internal对象, 参数TransportFactoryContainer就是clientTransportsContainer

    7. 继续跟踪clientTransportsContainer:

    /*
     * Transport factory container for transports this bus attachment uses to communicate with the daemon.
     */ 
    static class ClientTransportFactoryContainer : public TransportFactoryContainer {
      public:
        ClientTransportFactoryContainer() : transportInit(0) { }
    
        void Init()
        {
            /*
             * Registration of transport factories is a one time operation.
             */
            if (IncrementAndFetch(&transportInit) == 1) {
                if (ClientTransport::IsAvailable()) {
                    Add(new TransportFactory<ClientTransport>(ClientTransport::TransportName, true));
                }
                if (NullTransport::IsAvailable()) {
                    Add(new TransportFactory<NullTransport>(NullTransport::TransportName, true));
                }
            } else {
                DecrementAndFetch(&transportInit);
            }
        }
    
      private:
        volatile int32_t transportInit;
    } clientTransportsContainer;
    

    可以看到这是一个静态类,在初始化时检查是否支持NullTransport,如果支持就将它添加到TransportFactoryContainer中,在start时就会创建对应的transport 对象,供connect进行连接。

    所以,现在问题的关键在于:NullTransport::IsAvailable()是否返回true

    8. 跟踪NullTransport::IsAvailable()函数:

    /**
         * The null transport is only available if the application has been linked with bundled daemon
         * support. Check if the null transport is available.
         *
         * @return  Returns true if the null transport is available.
         */
        static bool IsAvailable() { return daemonLauncher != NULL; }
    
    该函数通过检查daemonLauncher 变量是否为空,来决定是否支持NullTransport。其声明如下:
    class NullTransport : public Transport { 
    private:
    ...
    static DaemonLauncher* daemonLauncher; /**< The daemon launcher if there is bundled daemon present */
    };
    

    初始化:

    DaemonLauncher* NullTransport::daemonLauncher;

    可知:daemonLauncher变量默认为空,即默认是不支持NullTransport的。

    9. 检查整个工程代码,发现为其赋值的代码如下:

    BundledDaemon::BundledDaemon() : transportsInitialized(false), stopping(false), ajBus(NULL), ajBusController(NULL)
    {
        NullTransport::RegisterDaemonLauncher(this);
    }
    void NullTransport::RegisterDaemonLauncher(DaemonLauncher* launcher)
    {
        daemonLauncher = launcher;
    }
    

    说明:只有在声明BundledDaemon对象的情况下daemonLauncher才不为空,才能支持NullTransport(即Bundled Daemon 模式)。

    10. 查看文件daemon/bundled/BundledDaemon.cc230行,发现已声明BundledDaemon 的静态对象

    static BundledDaemon bundledDaemon;

    这是不是意味着,只需要连接该文件就可以声明BundledDaemon 对象, daemonLauncher才不为空,进而支持NullTransport(即Bundled Daemon 模式)?

    以AllJoyn自带的chat做实验,位于:alljoyn-3.3.0-src/build/linux/x86-64/debug/dist/samples/chat

    修改Makefile的连接选项,发现只需要按如下顺序添加连接选项:

    LIBS = -lalljoyn ../../lib/BundledDaemon.o -lajdaemon -lstdc++ -lcrypto -lpthread –lrt

    程序就会自动启用Bundled Daemon,而不需要手动启动alljoyn-daemon进程

    $ ./chat -s a
    BusAttachment started.
    RegisterBusObject succeeded.
       0.063 ****** ERROR NETWORK external          Socket.cc:249                 | Connecting (sockfd = 15) to @alljoyn : 111 - Connection refused: ER_OS_ERROR
       0.063 ****** ERROR ALLJOYN external          posix/ClientTransport.cc:258  | ClientTransport(): socket Connect(15, @alljoyn) failed: ER_OS_ERROR
    Using BundledDaemon
    AllJoyn Daemon GUID = 8216a0fc60832b5f50c2111527f89fc1 (2MWyW3hV)
    StartListen: tcp:r4addr=0.0.0.0,r4port=0
    StartListen: ice:
    Connect to ‘null:’ succeeded-----------------------------已经连上NullTransport
    

      

    最终结论:

    应用程序如果想使用Bundled Daemon,只需要在连接选项中添加如下库即可(不可改变连接库顺序):

    -lalljoyn ../../lib/BundledDaemon.o -lajdaemon

      

      

      

  • 相关阅读:
    wxPython跨线程调用
    安卓开发24:FrameLayout布局
    URAL 1081
    [置顶] Hibernate运行机理
    [置顶] Hibernate的一个经典异常
    poj1190 生日蛋糕 dfs
    [置顶] 自己写代码生成器之生成Dal层代码(获取数据库所有表名称)
    修改mysql数据存储的地址
    拖延心理学
    DeepLearnToolbox使用总结
  • 原文地址:https://www.cnblogs.com/chutianyao/p/3202454.html
Copyright © 2011-2022 走看看