Java进行SNMP通信的指南.docx
《Java进行SNMP通信的指南.docx》由会员分享,可在线阅读,更多相关《Java进行SNMP通信的指南.docx(22页珍藏版)》请在冰豆网上搜索。
Java进行SNMP通信的指南
Java进行SNMP通信的指南
1.概述
在网络通信中,我们会经常遇到支持SNMP协议的网络设备打交道,支持SNMP协议的网络设备有很多,如各种带操作系统的服务器、路由器、交换机等,装有UNIX操作系统中的服务器一般都会支持SNMP协议,Windows操作系统的服务器也可以通过”控制面板->添加/删除程序->添加/删除Windows组件->管理和监视工具”中安装SNMP组件,如下图所示。
图1-1
本文的目的不在于讲解SNMP的原理,关于SNMP的原理及其信息组织结构MIB的详细资料,网上的资料很多,本文的目的就是针对在Java环境下解决如何实现SNMP客户端的调用操作以及如何实现SNMP服务器的简单模拟,把笔者的一些经验分享给大家。
2.实现过程
我们知道,SNMP协议是基于UDP协议的,RFC-1157就是SNMP协议详细规范,SNMP协议经历了3个版本,分别是SNMPV1、SNMPV2和SNMPV3。
SNMPV1是一种简单的请求/响应协议,网络管理系统发出一个请求,管理器则返回一个响应,支持GET、GETNEXT、SET和TRAP这4种操作;SNMPV2在SNMPV1的基础上还新增了两种新操作,GETBULK和INFORM;SNMPV3中增加了安全管理方式及远程控制。
由于SNMPV3的安全控制设置起来比较麻烦,所以一些被管设备不支持SNMPV3,而SNMPV1对于大数据量处理有缺陷,所以大部分被管设备都支持SNMPV2,应用得比较多的也是SNMPV2。
JDK提供了对于UDP的编程的支持,分别是数据报套接字(DatagramSocket)和数据包(DatagramPacket),直接采用这些类可以实现对SNMP协议的操作调用,但是正如西方国家一句谚语“不要重复发明轮子(Don’tReinventtheWheel)”,我们没有必要从头开始在设计如何通过Java来实现SNMP,开源源代码的阵营里有好多工具包已经帮我们做掉这件事情了,其中比较出名的有joeSNMP、SNMP4J、iReasoningJavaSNMPAPI等,笔者采用的是SNMP4J,利用SNMP4J可以实现多种SNMP的应用,如客户端调用、Trap的收发、服务端模拟,下面我们就详细讲解如何通过SNMP4J来实现客户端及模拟器(服务端)的应用。
2.1.SNMP4J介绍
我们用到了SNMP4J的几个基础类:
org.snmp4j.TransportMapping;
org.snmp4j.Snmp;
org.snmp4j.smi.Address;
org.snmp4j.Target;
TransportMapping类是对传输层的封装,对UDP封装为DefaultUdpTransportMapping,TCP封装为DefaultTcpTransportMapping,TransportMapping只定义传输的接口,由于SNMP默认采用UDP作为传输协议,所以笔者感觉DefaultTcpTransportMapping不会被用到。
Snmp类是SNMP4J的核心,它提供了发送和接收SNMPPDUs的方法,所有的SNMPPDU类型都可以采用同步或者异步的方式被发送。
PDU.javaimplementsBERSerializable//SNMPv2的报文,提供了编码时需要的信息(个人觉得编码信息可以由工具类提供,对用户会混淆)。
报文结构参见ScopedPDU(v3)。
AddressIP地址和端口(和的不同),常用实现是UdpAddress。
Target发送的时候要用到,包含Address,超时、重试次数、SNMP的版本,常用实现是ComunityTarget,可以指定readcommunity及writecommunity。
2.2.实现客户端
2.2.1.初始化
首先定义类变量,DATATYPE常量的定义是在SET操作里用到的,代码如下:
代码:
/**TransportMapping*/
privateTransportMappingtransport;
/**プロトコール*/
privateSnmpprotocol;
/*DateType定義*/
/**Counter32*/
publicstaticfinalintDATATYPE_COUNTER32=0;
/*Counter64*/
publicstaticfinalintDATATYPE_COUNTER64=1;
/**Gauge32*/
publicstaticfinalintDATATYPE_GAUGE32=2;
/**GenericAddress*/
publicstaticfinalintDATATYPE_GENERICADDRESS=3;
/**Integer32*/
publicstaticfinalintDATATYPE_INTEGER32=4;
/**IpAddress*/
publicstaticfinalintDATATYPE_IPADDRESS=5;
/**OctetString*/
publicstaticfinalintDATATYPE_OCTETSTRING=6;
/**TimeTicks*/
publicstaticfinalintDATATYPE_TIMETICKS=7;
/**UnsignedInteger32*/
publicstaticfinalintDATATYPE_UNSIGNEDINTEGER32=8;
代码段2.2.1-1
初始化,代码如下:
代码:
/**
*初期化
*
*@throwsSmsTerminalException
*アプリエラー
*/
privatevoidinit()throwsSmsTerminalException{
try{
transport=newDefaultUdpTransportMapping();
protocol=newSnmp(transport);
}catch(IOExceptionex){
thrownewSmsTerminalException("initerror",Constants.EXIT_CODE_NORMAL,ex);
}
}
代码段2.2.1-2
2.2.2.GET/GETNEXT操作
SnmpVO是一个简单的JavaBean,包含了以下属性
∙ipAddress(目标机器IP地址);
∙port(端口);
∙retry(重试次数);
∙timeout(超时时间);
∙oid(要获取属性的OID);
∙type(操作类型,默认是GET);
∙communityGet(GET操作的团体字符串);
∙communitySet(SET操作的团体字符串);
GET/GETNEXT操作的代码如下:
代码:
/**
*MIB情報を取得する
*
*@paramsnmpVo
*SNMP相関情報
*@returnoutValue
*SNMP取得結果
*
*@throwsSmsTerminalException
*アプリエラー
*/
publicStringgetSnmpValue(SnmpVOsnmpVo)throwsSmsTerminalException{
log.debug("getSnmpValuestart");
StringoutValue="";
try{
CommunityTargetmyTarget=setTarget(snmpVo,false);
transport.listen();
PDUrequest=setRequest(snmpVo);
PDUresponse=null;//PDUresponse
ResponseEventresponseEvent=protocol.send(request,myTarget);
if(snmpSendReceiveListener!
=null){
snmpSendReceiveListener.beforeReceive();
}
//SNMP受信
response=responseEvent.getResponse();
if(response!
=null){
if(response.getErrorIndex()==PDU.noError
&&response.getErrorStatus()==PDU.noError){//正常場合
VariableBindingvb=response.get(0);
if(vb.isException()){
thrownewSmsTerminalException(vb.getVariable().toString(),
Constants.EXIT_CODE_NORMAL,responseEvent.getError());
}
outValue=vb.getVariable().toString();
}else{//エラー発生
thrownewSmsTerminalException(responseEvent.getError().getMessage(),
Constants.EXIT_CODE_NORMAL,responseEvent.getError());
}
}else{//タイムアウト
thrownewSmsTerminalTimeoutException("SNMP受信タイムアウト");
}
}catch(IOExceptionex){
thrownewSmsTerminalException("getSnmpValueIOerror",Constants.EXIT_CODE_NORMAL,ex);
}finally{
try{
if(protocol!
=null){
protocol.close();
}
if(transport!
=null&&transport.isListening()){
transport.close();
}
}catch(IOExceptionex){
thrownewSmsTerminalException("closeprotocolortransporterror",
Constants.EXIT_CODE_NORMAL,ex);
}
}
log.debug("getSnmpValueend");
returnoutValue;
}
代码段2.2.2-1
其中用到了setTarget方法,采用ComunityTarget设置目标设备IP和端口、Community、SNMP版本、重试次数、超时时间,代码如下:
代码:
/**
*CommunityTargetの設定
*
*@paramsnmpVo
*SNMP相関情報
*@paramisSetOperationSET操作かどうか
*@returnmyTarget
*設定したCommunityTarget
*
*/
privateCommunityTargetsetTarget(SnmpVOsnmpVo,booleanisSetOperation){
CommunityTargetmyTarget=newCommunityTarget();
AddressdeviceAdd;
deviceAdd=GenericAddress.parse(snmpVo.getIpAddress()+"/"
+snmpVo.getPort());
myTarget.setAddress(deviceAdd);//address
if(isSetOperation){
if(StringConverter.nullCheckString(snmpVo.getCommunitySet())){
myTarget.setCommunity(newOctetString(snmpVo.getCommunitySet()));//communitySet
}
}else{
if(StringConverter.nullCheckString(snmpVo.getCommunityGet())){
myTarget.setCommunity(newOctetString(snmpVo.getCommunityGet()));//communityGet
}
}
if(snmpVo.getRetry()!
=0){
myTarget.setRetries(snmpVo.getRetry());//retries
}
if(snmpVo.getTimeout()!
=0){
myTarget.setTimeout(snmpVo.getTimeout());//timeout
}
//intversion=SnmpConstants.ver;
if(StringConverter.nullCheckString(snmpVo.getCommunityGet())
&&("2c".equals(snmpVo.getVer())||"1".equals(snmpVo.getVer()))){
myTarget.setVersion(SnmpConstants.version2c);//org.snmp4j.mp.*
}
returnmyTarget;
}
代码段2.2.2-2
还有setRequest方法,代码如下:
代码:
/**
*OIDの設定
*
*@paramsnmpVo
*SNMP相関情報
*@returnoutValue
*設定したVariableBinding
*
*/
privatePDUsetRequest(SnmpVOsnmpVo){
PDUrequest=newPDU();
VariableBindingvar;
//oidを設定する
var=newVariableBinding(newOID(snmpVo.getOid()));
request.add(var);
//情報取得方式を設定する
request.setType(snmpVo.getType());
if(snmpVo.getType()==0){
request.setType(PDU.GET);//DefaultPDUtypeisGET.
}
returnrequest;
}
代码段2.2.2-3
2.2.3.SET操作
Set操作的代码如下:
代码:
/**
*MIB情報を設定する
*@authorhonghui
*@paramsnmpVo
*SNMP相関情報
*@paramvalue
*SNMP設定内容
*@paramdataTypeデータのタイプ
*@throwsSmsTerminalException
*アプリエラー
*/
publicvoidsetSnmpValue(SnmpVOsnmpVo,Stringvalue,intdataType)
throwsSmsTerminalException{
log.debug("setSnmpValuestart");
try{
CommunityTargetmyTarget=setTarget(snmpVo,true);
transport.listen();
PDUrequest=newPDU();
OIDoid=newOID(snmpVo.getOid());
Variablevariable=getDataTypeVariable(value,dataType);
if(variable==null){
thrownewSmsTerminalException("InvaliddataType:
"+dataType,
Constants.EXIT_CODE_NORMAL,null);
}
request.add(newVariableBinding(oid,variable));
request.setType(PDU.SET);
PDUresponse=null;//PDUresponse
ResponseEventresponseEvent=protocol.send(request,myTarget);
if(snmpSendReceiveListener!
=null){
snmpSendReceiveListener.beforeReceive();
}
//SNMP受信
response=responseEvent.getResponse();
if(response!
=null){
if(response.getErrorIndex()==PDU.noError
&&response.getErrorStatus()==PDU.noError){
//正常場合
}else{//エラー発生
thrownewSmsTerminalException(responseEvent.getError().getMessage(),
Constants.EXIT_CODE_NORMAL,responseEvent.getError());
}
}else{//タイムアウト
thrownewSmsTerminalTimeoutException("SNMP受信タイムアウト");
}
}catch(IOExceptionex){
thrownewSmsTerminalException("setSnmpValueIOerror",Constants.EXIT_CODE_NORMAL,ex);
}finally{
try{
if(protocol!
=null){
protocol.close();
}
if(transport!
=null&&transport.isListening()){
transport.close();
}
}catch(IOExceptionex){
thrownewSmsTerminalException("closeprotocolortransporterror",
Constants.EXIT_CODE_NORMAL,ex);
}
}
}
代码段2.2.3-1
Set操作调用到了getDataTypeVariable方法,需要把传入的dataType转换成SNMP4J的Variable,代码如下:
代码:
/**
*Variableを変換です
*@authorhonghui
*@paramvalueSNMP設定内容
*@paramdataTypeデータのタイプ
*@returnVariable
*/
privateVariablegetDataTypeVariable(Stringvalue,intdataType){
switch(dataType){
caseDATATYPE_COUNTER32:
returnnewCounter32(Long.parseLong(value));
caseDATATYPE_COUNTER64:
returnnewCounter64(Long.parseLong(value));
caseDATATYPE_GAUGE32:
returnnewGauge32(Long.parseLong(value));
caseDATATYPE_GENERICADDRESS:
returnnewGenericAddress();
caseDATATYPE_INTEGER32:
returnnewInteger32(Integer.parseInt(value));
caseDATATYPE_IPADDRESS:
returnnewIpAddress(value);
caseDATATYPE_OCTETSTRING:
returnnewOctetString(value);
caseDATATYPE_TIMETICKS:
returnnewTimeTicks(Long.parseLong(value));
caseDATATYPE_UNSIGNEDINTEGER32:
returnnewUnsignedInteger32(Long.parseLong(value));
}
returnnull;
}
代码段2.2.3-2
2.3.实现模拟器
模拟器(或者说是服务端)的作用很明显,就是在服务器上开启一个监听SNMP的服务,当有客户端的请求时,返回一些结果。
如果模拟器做的很完美的话,客户端连接某个服务器获取或设置SNMP信息时,并没有觉察服务器是真实的还是模拟出来的。
在我们的运用中,只是简单的模拟了GET、GETNEXT和SET这3个操作,对于GET/GETNEXT操作来说,请求后返回的数据也是从数据文件里读取到的,数据文件就是一个普通的CSV文件,内容如下:
数据文件(CSV):
OID,MIB情報
"1.3.6.1.2.1.1.6.0","Beijing2008"
"1.3.6.1.2.1.1.7.0","Olympic"
代码段2.3-1
由于我们只是简单的模拟,所以GET和GETNEXT操作返回的结果是相同的(如果要做成不同的,就要对OID进行排序处理)。
对于SET操作来说,没有返回,如果SET出错,就会抛出异常。
模拟器的代码不多,处理类的全部代码如下:
代码:
/**
*CMTS_SNMPシミュレーター処理クラス