基於WinSNMP的網路管理程式設計----原理與實踐(第三章) (轉)
第三章 Snmp在下的實現----WinSNMP原理
在Windows下實現SNMP的程式設計,可以採用Winsock介面,在161,162埠透過udp傳送資訊。在中,已經封裝了SNMP協議的實現,提供了一套可供在Windows下開發基於SNMP的管理的介面,這就是WinSNMP 。
3.1 什麼是WinSNMP
WinSNMP的目的是為在Windows下開發基於SNMP的網路管程式提供解決方案。它為SNMP網管開發者提供了必須遵循的開放式單一介面規範,它定義了過程、資料型別、資料結構和相關的語法。
圖3.1顯示了一個網路管理站(NMS)和網路管理(Agent)之間端到端的SNMP連線中WinSNMP所處的層次。這是一個WinSNMP的參考模型。
圖3.1WinSNMP參考模型
總的來說,WinSNMP以的形式封裝了SNMP協議的各部分(在VC++6.0開發環境中體現為wsn2.dll、wsnmp32.lib和winsnmp.h),且針對SNMP是使用UDP的特點而設定了訊息重傳、超時機制等。
3.2 一些基本概念
在WinSNMP程式設計中,我們需要考慮的基本概念主要有以下幾點:
SNMP支援層次
Entity/Context轉換
本地
會話
非同步模式
管理
下面我們將分別對它們作介紹。
3.2.1 SNMP支援層次(Levels of SNMP Support)
WinSNMP支援四個層次的SNMP操作:
Level 0 = 只有訊息編碼/解碼
Level 1 = Level 0 + 與SNMPv1代理的通訊
Level 2 = Level 1 + 與SNMPv2代理的通訊
Level 3 = Level 2 + 與其它SNMPv2管理站的通訊
因為SNMP協議支援SNMPv1與SNMPv2的共存,所以WinSNMP實現能提供對兩個版本協議的支援。
SnmpStartup函式能返回當前WinSNMP實現所能提供的最大支援層次。
3.2.2 Entity/Context轉換模式(Entity/Context Translation Modes)
WinSNMP應用程式能夠讓WinSNMP實現把entity和context引數按不同的方式解釋:
(1)按字面解釋為SNMPv1代理的地址和共同體(community)字串。
(2)解釋為SNMPv2的party和context識別符號(context IDs)。
(3)透過查詢本地資料庫將其轉換為各自的SNMPv1或SNMPv2元素。
三種Entity/Context轉換模式如下:
SNMPAPI_TRANSLATED = 透過本地資料庫查詢轉換
SNMPAPI_UNTRANSLATED_V1 = 轉換為地址和共同體(community)字串
SNMPAPI_UNTRANSLATED_V2 = SNMPv2的party和context IDs.
我們可以透過SnmpStartup函式獲得當前預設的entity/context轉換模式,SnmpSetTranslatedMode函式可以用來設定entity/context轉換模式。
當在中採用SNMPv1協議時,我們可以將其設定為SNMPAPI_UNTRANSLATED_V1,具體實現如下:
HSNMP_ENTITY hAgent;
HSNMP_CONTEXT hView;
LPCSTR entityName = “202.120.86.71”;
smiOCTETS contextName;
contextName.ptr = “public”;
contextName.len = lstrlen (contextName.ptr);
hAgent = SnmpStrToEntity (hSomeSessin, entityName);
hView = SnmpStrToContext (hSomeSession, const &contextName);
透過這樣的設定,我們就可以在161埠透過UDP訪問“202.120.86.71”上的SNMP代理了。
3.2.3 本地資料庫(Local Database)
本地資料庫主要重傳模式(RetransmitMode)、重試次數(Retry)、超時(timeout)、轉換模式(TranslateMode)等值。我們可以對其中的資料進行讀(get)、寫(set)操作。
3.2.4 會話(session)
會話是用來管理WinSNMP應用程式和WinSNMP實現之間的連線,由SnmpCreateSession(推薦)或SnmpOpen函式建立。會話是資源管理的最小單位,也是WinSNMP應用程式和WinSNMP實現之間通訊管理的最小單位。一個良好的WinSNMP應用程式應該使用會話結構邏輯地管理它的操作,並將實現中的資源需求控制在最小。
呼叫SnmpCreateSession或SnmpOpen函式建立一個會話時,會返回一個“session id”,這是一個控制程式碼(handle)變數,WinSNMP用它來管理自己的資源。應用程式最終應呼叫SnmpClose函式將會話釋放。
3.2.5 非同步模式(Asynchronous Model)
當代程式設計模式的一個很大特點就是訊息。WinSNMP採用了非同步訊息驅動模式,主要基於兩個原因:
(1) 非同步訊息驅動模式非常適合於面向理論、SNMP分散式管理模型以及Windows程式設計、執行環境。
(2) SNMP再管理站和代理之間傳送資料沒有什麼特別的傳輸機制,它基本上是基於資料包的,沒有在實體之間建立實際通道(虛電路)。這樣的事實使得WinSNMP非常適合採用非同步模式。
現代的訊息驅動程式必須響應各種重要事件,有些則完全依賴於非同步關係。事實上,WinSNMP API中幾乎所有函式都有非同步成分,有些則是完全非同步的。有三個非常重要的非同步函式:
SnmpSendMsg (傳送資料)
SnmpRecvMsg (接收資料)
SnmpRegister (註冊接受trap訊息)
WinSNMP的整個程式設計模式就是基於非同步的,我們將在後面做詳細介紹。
3.2.6 記憶體管理(Memory Management)
在Windows程式設計中,記憶體管理一向是一個令人頭疼的問題。在這裡,我們將對WinSNMP的記憶體管理做一個較為詳盡的描述。
WinSNMP包括三種不同的記憶體“物件”:
控制程式碼式資源 (HANDLE’d Res)
C風格(以NULL結尾)的字串
WinSNMP API結構型別
3.2.6.1 控制程式碼式資源 (HANDLE’d Resources)
有五種控制程式碼式資源的變數:
Sessions
Entities
Contexts
Protocol Data Units (PDUs)
VarBindLists (VBLs)
所有控制程式碼物件都表示為“HSNMP_<_tag>”的形式,它為WinSNMP實現(以DLL方式)所擁有。
3.2.6.2 C風格字串 (C-Stytle Strings)
C風格的字串主要用來為通用的字串表示與Entity和物件識別符號(OID)物件之間的轉換提供便利。WinSNMP中使用C風格字串的函式有:SnmpStrToEntity、SnmpEntityToStr、SnmpStrToOid、SnmpOidToStr。
C風格字串的記憶體分配、管理和釋放完全由應用程式負責。因此我們還需要傳遞“size”引數給使用它的函式。
3.2.6.3 描述符 (Descriptors)
WinSNMP中有三種結構型別:
smiOCTETS
smiOID
smiVALUE
前兩種型別的定義如下:
typedef struct {
smiUINT32 len; /*unsigned long integer 型別,表示ptr中的位元組數*/
smiLPBYTE ptr; /*指向包含octet string的位元組陣列的far指標*/
} smiOCTETS;
typedef struct {
smiUINT32 len; /**unsigned long integer 型別,表示ptr中無符號長整形的個數*/
smiLPUINT32 ptr; /*指向由OID各個識別符號組成的無符號長整形數祖的far指標*/
} smiOID;
smiVALUE稍微複雜一點,它的定義如下:
typedef struct { /* smiVALUE portion of VarBind */
smiUINT32 syntax; /* Insert SNMP_SYNTAX_
union {
smiINT sNumber; /* SNMP_SYNTAX_INT
SNMP_SYNTAX_INT32 */
smiUINT32 uNumber; /* SNMP_SYNTAX_UINT32
SNMP_SYNTAX_CNTR32 SNMP_SYNTAX_GAUGE32 SNMP_SYNTAX_TIMETICKS */
smiCNTR64 hNumber; /* SNMP_SYNTAX_CNTR64 */
smiOCTETS string; /* SNMP_SYNTAX_OCTETS
SNMP_SYNTAX_BITS
SNMP_SYNTAX_OPAQUE
SNMP_SYNTAX_IPADDR
SNMP_SYNTAX_NSAPADDR */
smiOID oid; /* SNMP_SYNTAX_OID */
smiBYTE empty; /* SNMP_SYNTAX_NULL
SNMP_SYNTAX_NOSUCHOBJECT
SNMP_SYNTAX_NOSUCHINSTANCE
SNMP_SYNTAX_ENDOFMIBVIEW */
} value; /* union */
} smiVALUE;
當一個應用程式得到一個smiVALUE變數時,首先必須檢查它的“syntax”成員,已決定怎樣取到它的第二個成員。
當“syntax”成員變數顯示“value”值是一個smiOCTETS或smiOID物件時,我們就應該考慮記憶體管理,約定如下:
(1) 當其作為輸入引數時,應用程式負責為變長物件分配記憶體;
(2) 當其作為輸出引數時,由WinSNMP實現(表現為DLL)為變長物件分配
記憶體。
3.2.6.4 記憶體的釋放
WinSNMP應用程式必須負責釋放所有透過呼叫WinSNMP API函式所分配的資源,主要有以下三類函式:
SnmpFree
SnmpClose : 關閉會話
SnmpCleanup : 必須在程式結束之前呼叫,釋放所有資源
應用程式推薦使用上述的順序來釋放所有的WinSNMP資源。
3.3 WinSNMP基本程式設計模式
WinSNMP API按照SNMP協議封裝了各種操作,包括PDU、VarBindList以及協議操作的各項函式。我們可以按照SNMP協議的描述,呼叫WinSNMP相關函式,完成一次完整的SNMP。我們下面將以筆者完整的系統(採用SNMPv1協議)為例,具體描述WinSNMP的一般程式設計模式。我們分傳送請求訊息與接受響應訊息兩部分來實現。
3.3.1 WinSNMP傳送請求訊息
WinSNMP傳送請求訊息的過程可以分為四個部分,主要有:WinSNMP的初始化、PDUs的建立、傳送資訊以及資源的釋放。
3.3.1.1 WinSNMP的初始化
(1) 呼叫SnmpStartup函式啟動WinSNMP。
(2) 呼叫SnmpCreateSession函式建立一個會話session。
(3) 呼叫SnmpSetRetransmitMode函式設定重傳模式。
(4) 呼叫SnmpSetRetry函式設定重傳次數。
(5) 呼叫SnmpSetTimeout函式設定超時時間。
其中第3、4、5步都是對本地資料庫的操作,完成了對WinSNMP相關引數的設定。
3.3.1.2 建立協議資料單元(PDUs)
在建立PDU之前,我們必須先建立變數繫結表(varbindlists)。
(1) 呼叫SnmpStrToOid函式建立讀取物件的OID,例如,我們建立MIB變數ipInReceives(一個例項的OID為1.3.6.1.2.1.4.3.0),我們可以採用下面的程式碼:
LPCSTR name="1.3.6.1.2.1.4.3.0";
smiOID Oid;
SnmpStrToOid(name,&Oid);
(2) 呼叫SnmpCreateVbl函式建立變數繫結表。
HSNMP_VBL m_hvbl=SnmpCreateVbl(session,&Oid,NULL);/*NULL表示該OID的值為空*/
(3) 呼叫SnmpSetVb函式往變數繫結表中新增變數繫結,我們需先創
建一個OID,命名為Oid。
SnmpSetVb(m_hvbl,0,&Oid,NULL);/*0表示往變數繫結表中新增變數繫結,非0值表示修改此位置的變數繫結*/
建立好了變數繫結表後,我們呼叫SnmpCreatePdu函式建立協議資料單元,在這個函式中,我們必須設定error_index、error_status、request_id引數,它們都與協議中相應的量對應。
HSNMP_PDU m_hpdu=SnmpCreatePdu(session,SNMP_PDU_GET,
NULL,NULL,NULL,m_hvbl);
3.3.1.3 傳送資訊
我們首先呼叫SnmpStrToContext和SnmpStrToEntity函式建立共同體(community)字串和代理entity,具體實現見3.2.2。
然後,我們呼叫SnmpSendMsg函式傳送資訊。
SnmpSendMsg(session,NULL,hAgent,hView,m_hpdu);
3.3.1.4 資源的釋放
最後,我們應該釋放所有分配的資源。
3.3.2 WinSNMP接受響應訊息
還記得前面的SnmpCreateSession函式嗎?它可以說是WinSNMP非同步訊息驅動模式的一個關鍵,讓我們先來看看它的函式原型:
HSNMP_SESSION SnmpCreateSession(
HWND hWnd, // handle to the notification window
UINT wMsg, // window notification message number
SNMPAPI_CALLBACK fCallback, // notification callback function
LPVOID lpClientData // pointer to callback function data
);
它提供了兩種方式的非同步訊息驅動,我們可以讓WinSNMP在有響應訊息到達時傳送一個訊息給系統,也可以讓它自動呼叫一個函式。筆者採用了第一種方式,實現如下:
session=SnmpCreateSession(m_hWnd,wMsg,NULL,NULL);
我們可以給訊息wMsg建立一個訊息處理函式,在這個函式里處理訊息的接收、資訊的提取與處理等事務。
下面我們將具體描述WinSNMP接受響應訊息的步驟。
(1) 呼叫SnmpRecvMsg函式接收資料
(2) 呼叫SnmpGetPduData函式從PDU中析取出資料,
(3) 呼叫SnmpCountVbl獲得變數繫結列表中變數繫結的個數
(4) 呼叫SnmpGetVb函式取得PDU變數繫結表中每個變數繫結的OID及其對應的值,可以指明該變數繫結在變數繫結表中的位置。參考實現如下:
int nCount=SnmpCountVbl(varbindlist);
for(int index=1;i<=nCount;i++)
SnmpGetVb(varbindlist,index,&Oid,value[i]);
其中,index指定了變數繫結的位置,value[i]表示接收到的OID變數的值,是smiLPVALUE型別的,Oid表示接收到的變數繫結的OID。
對於value[i],我們可以參考3.2.6.3節,按照它的syntax成員,用 case語句,分別轉換為字串或整數型別。
(5) 呼叫SnmpOidToStr函式將Oid轉換為字串。並將接收到的Oid與傳送資料包的各OID做比較,已決定各自值的歸屬。引用一段程式碼
if(strcmp(m_sOid[i],m_initOid[1])==0)
m_sDesr= str[i];
else if (strcmp(m_sOid[i],m_initOid[2])==0)
m_sSysOid=str[i];
else if (strcmp(m_sOid[i],m_initOid[3])==0)
m_sSysTime=str[i];
else if (strcmp(m_sOid[i],m_initOid[4])==0)
m_sName=str[i];
else if (strcmp(m_sOid[i],m_initOid[5])==0)
{m_sIpin=str[i];
m_nIpin=nIpin;}
else if(strcmp(m_sOid[i],m_initOid[6])==0)
m_sIpout=str[i];
當我們比較傳送的OID與接收到的OID時,我們就知道了這個str[i]是屬於哪個OID的值,應當放在哪裡顯示,以m_s開頭的變數都代表了不同的label,這樣,相應的值就在相應的字串中顯示。
透過這樣的步驟,我們就完成了一個簡單的SNMP網路管理程式的設計。但是,在具體的應用中,我們應該考慮更多的問題,如記憶體管理、錯誤處理等問題,還有很多問題需要我們在系統開發的過程中去發現、解決。下面,我將描述幾個我在系統開發中遇到的問題,有的已經解決,有的還在探索中,希望能為同仁提供參考。
3.4 幾個問題
3.4.1 讀IP地址
前面講到,IpAddress是SMIv1的一個應用資料型別,表示IP地址,它的定義為:
IpAddress::=[APPLICATION 0] IMPLICIT OCTET STRING(SIZE(4))
當我們讀取一個表示IP地址的OID時,我們應該分別讀出IpAddress四個位元組的值,再將它們處理成我們平時見到的IP地址的形式。程式碼如下:
case SNMP_SYNTAX_IPADDR:
strIp.Format("%d",*m_value[i]->value.string.ptr);
strIp+=".";
strTemp.Format("%d",*(m_value[i]->value.string.ptr+1));
strIp+=strTemp;
strIp+=".";
strTemp.Format("%d",*(m_value[i]->value.string.ptr+2));
strIp+=strTemp;
strIp+=".";
strTemp.Format("%d",*(m_value[i]->value.string.ptr+3));
strIp+=strTemp;
3.4.2 GETNEXT操作的實現
GETNEXT是SNMP中用來讀取表格變數的一個操作。在WinSNMP中,我們可以透過SnmpCreatePdu(session,SNMP_PDU_GETNEXT,NULL,NULL,NULL,m_hvbl)來建立一個GETNEXT操作的PDU。
關鍵的問題是我們如何對這個表格作遍歷。(1).如何判斷表格的結束;(2).在接收到響應訊息時如何處理。
我們下面將以筆者系統為例,說明這些問題。我們將獲得本機的表的一部分。先構造一個函式,程式碼如下:
void CSnmpManagerDlg::Next(LPTSTR Oid)
{
CString str(Oid);
if(!strcmp(str.Left(20),"1.3.6.1.2.1.4.21.1.7"))
{
理接收到的資料
pSnmp.CreateVbl(Oid,NULL);
pSnmp.CreatePdu(SNMP_PDU_GETNEXT,NULL,NULL,NULL);
pSnmp.Send("127.0.0.1","public");
}
else
{
m_bNext=FALSE;
去顯示
}
}
我們把接收到的OID的前20位與路由next hop MIB變數("1.3.6.1.2.1.4.21.1.7")作比較,假如不等,就說明這一列已經結束。把資料送去顯示或進一步處理。
我們可以為這一操作建立一個新的會話(session),或繼續使用前面GET操作的會話。建立一個新的會話時,我們為這個會話指定一個訊息處理函式,並在這個函式中,處理接收到的資料,以及呼叫Next(LPTSTR Oid)函式繼續傳送GETNEXT操作。
假如繼續使用以前的會話,我們要依靠標誌m_bNext,判斷m_bNext的真假以決定是否繼續發GETNEXT資料包。
void CSnmpManagerDlg::OnRecv()
{ 收、處理訊息
if(m_bNext==TRUE)
Next(m_sOid);
}
這樣,我們就完成了對錶格中一列的遍歷。同樣,我們可以完成對整個表格的遍歷,我們只需strcmp(str.Left(18),"1.3.6.1.2.1.4.21.1"),就可以獲得整個表格的結束。再在Next(LPTSTR Oid)函式中用switch-case語句按各個MIB變數的值分類,就可以得到整個表格。
3.4.3 對錶格變數的SET操作
在整個系統的開發中,我們曾經對SysName變數進行SET操作。證明是可行的。但當我們SET一個表格變數時,報告變數繫結(VB)錯誤,型別為bad value。可能有兩個原因。
(1). 代理程式(Agent)不支援對這些表格變數的SET操作。(具體見1212)
(2). 當SET一個表格變數時,我們應該對錶格中的所有變數都賦值,並封裝成一個PDU發出去。因為當我們用route add新增路由表時,必須指定所有的引數。並且,表格變數只允許新增與刪除兩種操作。
來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/10748419/viewspace-1000537/,如需轉載,請註明出處,否則將追究法律責任。
相關文章
- 基於WinSNMP的網路管理程式設計----原理與實踐(摘要) (轉)程式設計
- 基於WinSNMP的網路管理程式設計----原理與實踐(目錄) (轉)程式設計
- 基於WinSNMP的網路管理程式設計----原理與實踐(第四章) (轉)程式設計
- 基於WinSNMP的網路管理程式設計----原理與實踐(第一章) (轉)程式設計
- Socket網路程式設計基礎與實踐:建立高效的網路通訊程式設計
- 圖片管理系統:原理、設計與實踐
- Java實驗——基於GUI的網路通訊程式設計JavaGUI程式設計
- 基於java的網路招聘系統的設計與實現Java
- C#非同步程式設計:原理與實踐C#非同步程式設計
- Java畢業設計_基於MySQL網盤管理系統的設計與實現JavaMySql
- 《Python程式設計與演算法基礎教程》第三章上機實踐Python程式設計演算法
- 孟巖談《C++程式設計原理與實踐》C++程式設計
- 網路程式設計原理與UDP實現程式設計UDP
- Java的網路功能與程式設計 一 (轉)Java程式設計
- 《網路安全原理與實踐》一1.9審計和改進
- Socket原理與程式設計基礎程式設計
- 關於程式設計師寫文件(網路轉載)程式設計師
- C 程式設計最佳實踐(轉)程式設計
- 基於區塊鏈的隱私計算 - 原理和實踐區塊鏈
- vivo 基於 JaCoCo 的測試覆蓋率設計與實踐
- 《網路安全原理與實踐》一2.5複習題
- 《網路安全原理與實踐》一1.2資產確定
- 基於雲端計算的網路威脅管理分析
- 基於OpenStack+Docker設計與實現CI/CD——基於Docker技術的CI&CD實踐Docker
- 《網路安全原理與實踐》一1.8網路安全體系結構的部署
- 基於 KubeSphere 的分級管理實踐
- 可程式設計網路卡晶片在滴滴雲網路的應用實踐程式設計晶片
- 網路程式設計與通訊原理程式設計
- Swift 網路安全原理及實踐Swift
- C++程式設計最佳實踐(轉)C++程式設計
- 百度基於雲原生的推薦系統設計與實踐
- 基於SSH培訓機構管理系統的設計與實現
- 基於KubeEdge的邊緣節點分組管理設計與實現
- 流程設計與實施——再造與管理(轉)
- 《網路安全原理與實踐》一2.1安全區介紹
- vivo網際網路機器學習平臺的建設與實踐機器學習
- Java:基於TCP協議網路socket程式設計(實現C/S通訊)JavaTCP協議程式設計
- 基於java的企業車輛管理系統的設計與實現Java