原創做品,轉載請註明出處,嚴禁非法轉載。若有錯誤,請留言!css
email:40879506@qq.comhtml
爲了使程序具備通用性,便於擴展和維護。採用了"模塊"插入的思想。將設備業務相關的實現以動態庫的形式加載進來。java
在上篇文章已經介紹了CWMP的程序處理流程。本篇主要分析一下在CWMP core的程序裏如何加載lib庫。。好比如何實現調用so庫函數, 實現ACS URL解析, CPE get/set函數怎麼被調用,怎樣添加/刪除/更新 obj對象等。
git
一. 加載lib庫github
1) 打開動態鏈接庫bootstrap
還記得上節咱們定義的cwmp進程上下文結構體cwmp_context,使用dlopen以指定的模式打開動態庫文件,並返回設備library的handle。架構
//打開設備handle
cwmp_ctx->handle_lib = dlopen(cwmp_ctx->dev_info.dev_lib, RTLD_LAZY);socket
2) 調用設備相關函數ide
上節已經介紹了相關設備函數,並定義在device.xml。根據xml定義的tag頭取得函數名稱,並賦給CWMP進程上下文。好比模塊化
cwmp_ctx->dev_info.func_bootstrap = dlsym(cwmp_ctx->handle_lib, attr_value);
cwmp_ctx->dev_info.func_init = dlsym(cwmp_ctx->handle_lib, attr_value);
cwmp_ctx->dev_info.func_get_listenport = dlsym(cwmp_ctx->handle_lib, attr_value);
cwmp_ctx->dev_info.func_get_auth = dlsym(cwmp_ctx->handle_lib, attr_value);
cwmp_ctx->dev_info.func_url_dns_resolve= dlsym(cwmp_ctx->handle_lib, attr_value);
......
二. 設備相關初始化
1 //用於須要平臺一開始初始化 2 void dev_init(trf_param_t* param, callback_reg_func_t func, pthread_mutex_t *pmutex_param, LogFunc log_func) 3 { 4 pthread_t thd; 5 monitor_info_t *info = NULL; 6 7 //init local pointer 8 cwmplog_func = log_func; 9 g_reg_func = func; 10 g_root_param = param; 11 g_pmutex_param = pmutex_param; 12 13 info = (monitor_info_t *)malloc_check(sizeof(monitor_info_t)); 14 info->func = func; 15 info->param = param; 16 // info->log_func = log_func; 17 18 closeinout(); 19 20 //初始化CPE 與ACS 鏈接狀態 21 CpeSetValue(NULL, "0", "cpeagent.tr069.acs_status"); 22 23 /* 24 1. define user-defined event code in the device.xml. 25 */ 26 inform_bind(func); 27 28 // monitor_socket_event 線程函數用於與其餘程序或進程指定的socket進行通訊 29 // 好比源碼目錄下的sendSocket/client.c 程序,可用於測試或其餘 30 // CWMP_SOCK "/opt/cwmp.sock" 31 // pthread_create(&thd, NULL, monitor_socket_event, (void*)info); 32 return; 33 }
主要完成初始化操做:將內存中的根節點參數位置賦給動態庫中的全局變量g_root_param指針,以及初始化信號量,日誌記錄函數,以及FUNC回調函數。 inform_bind(func)函數實現用戶自定義的<EventCode>事件,好比電信運營商自定義了X CT-COM BIND事件類型,只用上報正確才能進行工單下發業務。
dev_bootstrap主要用來判斷是不是首次鏈接ACS,若是是把0 BOOTSTRAP和1 BOOT加入Inform事件中,不然把1 BOOT加入Inform事件中。
Inform中帶有以下結構信息:
<cwmp:Inform> <DeviceId xsi:type="cwmp:DeviceIdStruct"> <Manufacturer>TEST</Manufacturer> <OUI>A1B2C4</OUI> <ProductClass>TEST_PC</ProductClass> <SerialNumber>821281000054321</SerialNumber> </DeviceId> <Event SOAP-ENC:arrayType="cwmp:EventStruct[2]"> <EventStruct> <EventCode>1 BOOT</EventCode> <CommandKey></CommandKey> </EventStruct> <EventStruct> <EventCode>X CT-COM BIND</EventCode> <CommandKey></CommandKey> </EventStruct> </Event> <MaxEnvelopes>1</MaxEnvelopes> <CurrentTime>2017-01-09T11:53:00</CurrentTime> <RetryCount>0</RetryCount> <ParameterList SOAP-ENC:arrayType="cwmp:ParameterValueStruct[10]"> <ParameterValueStruct> <Name>InternetGatewayDevice.DeviceSummary</Name>
.......
三. 解析ACS URL
同時支持域名和ip地址解析。(放在後續高級部分專門講解)
四. 每一個節點RPC Method
節點結構體以下:
struct trf_param { char name[PARAM_NAME_LEN+1]; //參數名 int type; //參數類型 trf_datatype_e int writable; //是否可寫。0:不可寫,1:可寫,若是object //能夠Add,則可寫 int max_instance; //屬於Object, 最大instance值,-1表示無限制 int notification; //屬於Parameter, 0:off,1:passive,2:active unsigned char noti_rw; //屬於Parameter, 是否能夠設置上報屬性,0 不能夠 1 能夠 unsigned long acl; /*屬於Parameter, access list */ TRFGetParamValueFunc getparamval_func; //屬於Parameter, 取得參數值函數 TRFSetParamValueFunc setparamval_func; //屬於Parameter, 設置參數值函數 TRFAddObjectFunc addobject_func; //屬於Object, AddObject TRFDelObjectFunc delobject_func; //屬於Object, DeleteObject TRFRefreshFunc refresh_func; //屬於Object, 刷新 struct trf_param *parent; //父節點 struct trf_param *child; //子節點 struct trf_param *nextSibling; //兄弟節點 };
每一個參數節點擁有本身的屬性和方法,而且經過遞歸方式建立初始化參數樹,把初始化後的結果保存在cwmp_context進程上下文中
create_param(&cwmp_ctx->param_root, xmldata->doc->root->firstChild);
五. 其餘RPC方法
本文開頭已經介紹了調用設備相關函數的方法,其中包括除TR069規範中的升級,恢復出廠,Reboot,Download等方法,咱們也能夠經過」插件"的形式實現本身的或者私有廠商定義的方法。
<devlib name="/usr/lib/libcwmp.so"></devlib> <auth name="dev_get_auth"></auth> <listenport name="dev_get_listenport"></listenport> <wanparamname name="dev_get_wanparam_name"></wanparamname> <bootstrap name="dev_bootstrap"></bootstrap> <init name="dev_init"></init> <reboot name="dev_reboot"></reboot> <factoryreset name="dev_factoryreset"></factoryreset> <download name="dev_download"></download> <acsstatus name="dev_set_acs_status"></acsstatus> <urldnsresolve name="dev_url_dns_resolve"></urldnsresolve> <upload name="dev_upload"></upload> <cwmpenable name="dev_cwmp_enable"/>
六. 總結
tr069只是個協議棧,按照規範實現這個協議並不困難,在github上咱們也能夠去找到各類各用的開源程序,有Python寫的,java寫的,C寫的等等。可是,可以從開源程序中找到一個優秀的程序設計架構就如同大海撈針,很困難,更不要說商業用途了。
至此,已經基本寫完了關於程序設計部分的內容,本篇涉及的代碼比較多,儘可能不深刻代碼,而只是圍繞協議規範和程序模塊化思想分析了部分代碼的實現。
七. 參考
1)http://www.cnblogs.com/Anker/p/3746802.html
附:遺留問題
程序開發中在使用dl庫中遇到一個問題,如有心的讀者能解答,歡迎留言告訴我:
若dlopen打開的庫中不存在線程,或線程不在運行狀態,則調用dlclose時,進程不會死;
若存在運行時的線程(好比dev_init函數裏的 pthread_create(&thd, NULL, monitor_socket_event, (void*)info);),則dlclose時出現segmentation fault。
若不調用dlclose則會出現內存漏洞