记录snmp使用过程中的小问题。
1.maven工程依赖
<dependency> <groupId>org.snmp4j</groupId> <artifactId>snmp4j</artifactId> <version>2.8.7</version> </dependency>
2.网上找到的收发方法(V2c)
import org.snmp4j.CommunityTarget; import org.snmp4j.PDU; import org.snmp4j.Snmp; import org.snmp4j.TransportMapping; import org.snmp4j.event.ResponseEvent; import org.snmp4j.mp.SnmpConstants; import org.snmp4j.smi.*; import org.snmp4j.transport.DefaultUdpTransportMapping; import java.io.IOException; public class TestTrap { private Snmp snmp = null; private Address targetAddress = null; private TransportMapping<UdpAddress> transport = null; public void init() throws IOException { //目标主机的ip地址 和 端口号 (127.0.0.1是本机调试) targetAddress = GenericAddress.parse("udp:127.0.0.1/162"); transport = new DefaultUdpTransportMapping(); snmp = new Snmp(transport); transport.listen(); } /** * Snmp V2c 测试发送Trap * @return * @throws IOException */ public ResponseEvent sendV2cTrap() throws IOException { PDU pdu = new PDU(); VariableBinding v = new VariableBinding(); v.setOid(new OID("1.21.1.2.2.3.45.5.57567.3"));//随便写的oid v.setVariable(new OctetString("Snmp Trap V2 Test 123")); v.setOid(new OID("1.21.1.2.2.3.45.5.57567.4"));//随便写的oid v.setVariable(new OctetString("你好 Snmp Trap V2 Test 123")); pdu.add(v); pdu.setType(PDU.TRAP); // set target CommunityTarget target = new CommunityTarget(); target.setCommunity(new OctetString("public")); target.setAddress(targetAddress); // retry times when commuication error target.setRetries(2); target.setTimeout(1500); target.setVersion(SnmpConstants.version2c); // send pdu, return response return snmp.send(pdu, target); } public static void main(String[] args) { TestTrap poc = new TestTrap(); try { poc.init(); poc.sendV2cTrap(); } catch (IOException e) { e.printStackTrace(); } }
import cn.sh.ideal.service.NmsService; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.snmp4j.*; import org.snmp4j.mp.MPv2c; import org.snmp4j.smi.*; import org.snmp4j.transport.DefaultTcpTransportMapping; import org.snmp4j.transport.DefaultUdpTransportMapping; import org.snmp4j.util.MultiThreadedMessageDispatcher; import org.snmp4j.util.ThreadPool; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; import org.springframework.stereotype.Component; import javax.annotation.PostConstruct; import java.io.IOException; import java.net.BindException; import java.time.Duration; import java.time.Instant; /** * 本类用于监听代理进程的Trap信息 * 实现CommandResponder接口的监听器 */ @Component public class MultiThreadedTrapReceiver implements CommandResponder { private MultiThreadedMessageDispatcher dispatcher; private Snmp snmp = null; private Address listenAddress; private ThreadPool threadPool; @PostConstruct public void run() { try { init(); snmp.addCommandResponder(this);//注册命令响应监听器 System.out.println("开始监听Trap信息!"); } catch (BindException be) { System.out.println("--初始化snmp本地监听失败,地址/端口无法绑定,请确认本地snmp监听ip和端口配置 "+be.getMessage()); } catch (Exception ex) { System.out.println("--初始化snmp本地监听端口报错"+ex.getMessage()); ex.printStackTrace(); } } private void init() throws IOException { System.out.println("snmp udp localAddress:"+localAddress); //创建接收SnmpTrap的线程池,参数: 线程名称及线程数 threadPool = ThreadPool.create("TrapListen", "2"); dispatcher = new MultiThreadedMessageDispatcher(threadPool, new MessageDispatcherImpl()); //监听端的 ip地址 和 监听端口号 listenAddress = GenericAddress.parse(System.getProperty( "snmp4j.listenAddress", "udp:127.0.0.1/162")); TransportMapping<?> transport; if (listenAddress instanceof UdpAddress) { transport = new DefaultUdpTransportMapping((UdpAddress) listenAddress); } else { transport = new DefaultTcpTransportMapping((TcpAddress) listenAddress); } snmp = new Snmp(dispatcher, transport); snmp.getMessageDispatcher().addMessageProcessingModel(new MPv2c()); //开启Snmp监听,可以接收来自Trap端的信息。 snmp.listen(); } /** * 实现CommandResponder的processPdu方法, 用于处理传入的请求、PDU等信息 * @param respEvnt */ public void processPdu(CommandResponderEvent respEvnt) { // 解析Response if (respEvnt != null && respEvnt.getPDU() != null) { Vector<VariableBinding> recVBs = (Vector<VariableBinding>) respEvnt.getPDU().getVariableBindings(); for (int i = 0; i < recVBs.size(); i++) { VariableBinding recVB = recVBs.elementAt(i); System.out.println("OID: "+recVB.getOid() + " `,Variable: " + recVB.getVariable()); } } } }
3.问题
接收端输出日志中 Variable 不带中文时正常,带中文时会被转成16进制。
System.out.println(new OctetString("Snmp Test")); System.out.println(new OctetString("Snmp Test 你好"));
OctetString处理中文的锅
4.解决方案
public static String getChinese(String octetString) { //snmp4j遇到中文直接转成16进制字符串 String str = ""; try { String[] temps = octetString.split(":"); byte[] bs = new byte[temps.length]; for (int i = 0; i < temps.length; i++) { if (octetString.length() == 17) { str = str + temps[i] + "-"; } else { bs[i] = (byte) Integer.parseInt(temps[i], 16); } } if (octetString.length() == 17) { str = str.substring(0, str.length() - 1); } else { str = new String(bs, "UTF-8"); } } catch (Exception e) { return null; } return str; }
网上找的OctetString中文16进制字符串转回来方法,能转回中文。但是如果处理不带中文的OctetString会报错。
于是看了下OctetString类提供的方法,改了改目前用着没问题。
System.out.println("OID: "+recVB.getOid() + " `,Variable: " + getChinese((OctetString) recVB.getVariable()));
public static String getChinese(OctetString octetString) { //snmp4j遇到中文直接转成16进制字符串 if(octetString.isPrintable()){ return octetString.toString(); } return new String(OctetString.fromHexString(octetString.toString()).getValue()); }
2021-12-23追加一个小优化
请求端发Trap时Variable里面放OctetString没问题 但是放OID时,转格式 (OctetString) recVB.getVariable() 这里报错了 而且没有报错日志
加了个数据类型为OID判断的容错
System.out.println("OID: "+recVB.getOid() + " ,Variable: " + SnmpSendUtil.getChinese(recVB.getVariable()));
public static String getChinese(Variable obj) { //snmp4j遇到中文直接转成16进制字符串 if(obj instanceof OID) return obj.toString(); OctetString octetString = (OctetString)obj; if(octetString.isPrintable()){ return octetString.toString(); } return new String(OctetString.fromHexString(octetString.toString()).getValue()); }
5.划重点
目前还没搞明白snmp4j怎么实现服务端被GET时候返回PDU给请求端
snmp4j能实现服务端被GET么?是和被TRAP一样的通过监听端接收么?
有了解的大佬评论给指个路 谢谢(