Ice筆記-利用Ice::Application類簡化Ice應用

Ice筆記-利用Ice::Application類簡化Ice應用

作者:ydogg,轉載請申明。

 
在編寫Ice相關應用時,無論是Client還是Server端,都必須進行一些必要的動作,如:Ice通信器初始化、異常捕獲,以及應用終止後的銷燬。鑑於每個應用都需要,Ice運行時庫提供了Ice::Application類來解放用戶,避免重複勞動,消除繁瑣的初始化和銷燬細節。Ice::Application雖然實用,但總體來說是個比較簡單的類,主要提供了Ice通信器初始化和信號捕獲處理兩大功能。下面將從功能和實現兩方面進行闡述,並給出常見用法和注意事項。源碼版本爲Ice-3.2.1

 
一.Ice::Application概述

Ice::Application本身是一個抽象類,其run()函數爲純虛函數,因此必須被繼承後使用。
Ice::Application 是一個體(singleton,會個通信器。 如果你要使用多個通信器,不能使用Ice::Application來定義多個App。而至多定義一個App的實例。
其它通信器需要使用
Ice::initialize()手工生成。

 
二.Ice::Application的成員

Ice::Application無真正成員變量,其實際使用變量均在實現文件中以靜態形式提供。因此其提供的主要是靜態接口 

// Application的入口函數,提供了豐富的初始化方式,一般使用第一個
// 將應用主函數參數直接傳入即可
int main(intchar*[]);
int main(intchar*[], const char*);
int main(intchar*[], const Ice::InitializationData&);
int main(intchar*[], const char*const Ice::LoggerPtr&);
int main(const StringSeq&);
int main(const StringSeq&const char*);
int main(const StringSeq&const Ice::InitializationData&);

 
// 應用的執行循環,應用需要繼承這個函數並用自己的邏輯重寫
virtual int run(intchar*[]) = 0;

 
// 信號回調函數
// 如果需要自己對信號進行處理,則需要繼承和改寫這個函數
// 注意,需在run()函數中調用callbackOnInterrupt()來向Ice表示使用用戶回調
// 該函數的默認實現是空函數
virtual void interruptCallback(int);
 
// 返回應用名,即argv[0]
static const char* appName();

 
// 返回當前使用的Ice通信器實例指針
static CommunicatorPtr communicator();

 
// 設置信號處理模式
//
// 銷燬模式:信號到來時將通信器實例銷燬,也是Application的默認模式
static void destroyOnInterrupt();

// 關閉模式:信號到來時將通信器實例關閉,但不銷燬
static void shutdownOnInterrupt();

// 忽略模式:信號到來時將通信器不做任何處理
static void ignoreInterrupt();

// 用戶模式:信號到來時將調用interruptCallback()函數
static void callbackOnInterrupt();

 
// 信號的阻止和放開,不常用
// 阻塞信號的到來
static void holdInterrupt();

// 放開被阻塞的信號
static void releaseInterrupt();

 
// Application當前是否被信號中斷
// 可用於判斷Application的結束是否由於信號造成
static bool interrupted();

三.使用方法

一般直接初始化通信器的用法如下:

#include <Ice/Ice.h>
int main(int argc, char * argv[])
{
       
int status = 0;
       Ice::CommunicatorPtr ic;
       
try {
              ic 
= Ice::initialize(argc, argv);

              
// Server code here...

              
// ...

        } 
catch (const Ice::Exception & e) {
              cerr 
<< e << endl;
              status 
= 1;
       }

        
if (ic)
              ic
->destroy();
       
return status;
}
 
使用Ice::Application的代碼如下:

 


#include <Ice/Ice.h>
class MyApplication : virtual public Ice::Application
{
public:
       
virtual int run(intchar * []) {

       
// 如果需要,設置信號回調模式
              interruptCallback();
              
// ignoreInterrupt();

              
// Add Server code here...
              
// ...

              
return 0;
       }

     
      
virtual void interruptCallback(int{
             cout 
<< appName() << “ receive signal ” << endl;
      }

}
;

int main(int argc, char * argv[])
{

       MyApplication app;
       
return app.main(argc, argv);
}
可以看出,繁瑣的初始化細節已經不用考慮。抽象層次也更清晰一些。

四.實現分析

main的實現較多,但都是對函數
int main(int, char*[], const Ice::InitializationData&)的再包裝,其行爲
如下:

        建一個IceUtil::CtrlCHandler,適當地關通信器。

        保存傳入的argv[0]參數。以便通appName 函數,提供用的名字。

        初始化(通過調Ice::initialize。通過調用靜communicator()可以訪問當前使用的通信器。

        描參數向量,找與Ice run time 有關的選項,並移除這樣選項。因此,在傳給你的run 方法的參數向量中,不再有與Ice 有關的選項,而只有針對你的用的選項和參數。
實際上,
3,4步驟都由同一個函數Ice::initialize來完成。

        調用run()函數

        銷燬通信器(如果正常結束,沒有收到終止信號)

 
在以上過程中,main()函數還捕獲了幾乎全部異常,包括IceUtil::Exception,std::exception,甚至還有const char*const string&

函數代碼如下:

int
Ice::Application::main(
int argc, char* argv[], const InitializationData& initData)
{
    
// 不允許重複調用
    if(_communicator != 0)
    {
        cerr 
<< argv[0<< ": only one instance of the Application class can be used" << endl;
        
return EXIT_FAILURE;
    }
    
int status;

    
try
    {
        
// 設置信號捕捉器
         CtrlCHandler ctrCHandler;
        _ctrlCHandler 
= &ctrCHandler;

        
try
        {   
// 內部使用的條件變量初始化,主要用於信號阻塞
            if(_condVar.get() == 0)
            {
                _condVar.reset(
new Cond);
            }

           
// 初始化Ice通信器及其它變量(均爲靜態變量)
            _interrupted = false;
            _appName 
= argv[0];    // 設置應用名
                
            _application 
= this;
            _communicator 
= initialize(argc, argv, initData);
            _destroyed 
= false;

            
// 判斷應用是否提供了Ice.Nohup參數
           
// 如果Ice.Nohup大於0, Application會忽略SIGHUP(UNIX) 和
           //
CTRL_LOGOFF_EVENT (Windows). 因此,如果啓動應用的用戶註銷,
           // 設置了Ice.Nohup 的應用能繼續運行(只
適用於C++)。
             _nohup = (_communicator->getProperties()->getPropertyAsInt("Ice.Nohup"> 0);
        
            
// 收到信號的默認處理方式是銷燬通信器
            destroyOnInterrupt();
            status 
= run(argc, argv);
        }
        
catch(const IceUtil::Exception& ex)
        {
            cerr 
<< _appName << "" << ex << endl;
            status 
= EXIT_FAILURE;
        }
        
catch(const std::exception& ex)
        {
            cerr 
<< _appName << ": std::exception: " << ex.what() << endl;
            status 
= EXIT_FAILURE;
        }
        
catch(const std::string& msg)
        {
            cerr 
<< _appName << "" << msg << endl;
            status 
= EXIT_FAILURE;
        }
        
catch(const char* msg)
        {
            cerr 
<< _appName << "" << msg << endl;
            status 
= EXIT_FAILURE;
        }
        
catch(...)
        {
            cerr 
<< _appName << ": unknown exception" << endl;
            status 
= EXIT_FAILURE;
        }

        
// Application清理時,需要忽略所有信號
       ignoreInterrupt();
        {
            StaticMutex::Lock 
lock(_mutex);
            
while(_callbackInProgress)
            {
                _condVar
->wait(lock);
            }
            
if(_destroyed)
            {
                _communicator 
= 0;
            }
            
else
            {
                _destroyed 
= true;
                
//
                
// And _communicator != 0, meaning will be destroyed
                
// next, _destroyed = true also ensures that any
                
// remaining callback won't do anything
                
//
            }
            _application 
= 0;
        }

      
// 清理通信器(如果沒有通過信號清理過)
        if(_communicator != 0)
        {  
            
try
            {
                _communicator
->destroy();
            }
            
catch(const IceUtil::Exception& ex)
            {
                cerr 
<< _appName << "" << ex << endl;
                status 
= EXIT_FAILURE;
            }
            
catch(const std::exception& ex)
            {
                cerr 
<< _appName << ": std::exception: " << ex.what() << endl;
                status 
= EXIT_FAILURE;
            }
            
catch(...)
            {
                cerr 
<< _appName << ": unknown exception" << endl;
                status 
= EXIT_FAILURE;
            }
            _communicator 
= 0;
        }

        
//
        
// Set _ctrlCHandler to 0 only once communicator->destroy() has completed.
        
// 
        _ctrlCHandler = 0;
    }
    
catch(const CtrlCHandlerException&)
    {
        cerr 
<< argv[0<< ": only one instance of the Application class can be used" << endl;
        status 
= EXIT_FAILURE;
    }
   
    
return status;
}

IceUtil::CtrlCHandler的實現在IceUtil/CtrlCHandler.cpp中,其在windows下使用SetConsoleCtrlHandler()方式實現,可捕獲CTRL_C_EVENTCTRL_BREAK_EVENT、CTRL_CLOSE_EVENTCTRL_LOGOFF_EVENT以及CTRL_SHUTDOWN_EVENT信號。

linux下,通過pthread_sigmask()sigwait()配合實現,注意實現中使用了一個內部的獨立線程對信號進行捕獲。其選擇捕獲的信號有SIGHUPSIGINTSIGTERM其它Ice::Application的信號模式設置函數都是利用它來掛接自己的處理函數,來做出不同的動作。在此不再細述,請參見源碼。
 

五.參考文獻
Ice-1.3.0中文手冊(馬維達,感謝他的無私貢獻)
Ice-3.1.1英文手冊
Ice-3.2.1源碼

 
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章