記錄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一樣的通過監聽端接收麼?
有了解的大佬評論給指個路 謝謝(