锁定老帖子 主题:Swing版小小网管
该帖已经被评为精华帖
|
|
---|---|
作者 | 正文 |
发表时间:2010-08-26
JavaEye上不少朋友是做网管系统的。一个典型的网络管理系统,需要具备FCAPS几个标准模块,而网络的自动发现和拓扑展示是核心之一。很多人不喜欢Java的Swing,而本文就用一个很小很小的例子,来模拟一个小小的网络管理程序,希望能给大家一点启发。虽然很小,它却可以完成一个简单的局域网自动发现搜索、多线程、ICMP和SNMP的ping、节点的生成、拓扑的展示、自动布局等功能。继续改巴改巴也许还有点使用价值也未可知。
如果不喜欢研究代码,就当它是一个趣味程序吧!你可以在公司的网络里面搜索一把,把同事的机器都挖出来,看看你们公司的网络结构是怎样的;如果喜欢研究代码,可以看看相关SNMP、多线程和拓扑图展示的部分,虽然很简单,就当看肥皂剧消遣了。
Ping和SNMP PING 这个程序的自动发现比较简单,就是对所在的网段进行便利搜索。首先,获得本机的网址以及所在的网段。例如,如果本机的地址是192.168.1.122,那么所在的网段自然就是192.168.1.0。然后,将这个网段中所有可能存在的IP地址进行拆分,并通过多线程进行任务分配,一个一个的Ping。
public static boolean ping(String ip) { try { InetAddress ipaddress = InetAddress.getByName(ip); return ipaddress.isReachable(2000); } catch (Exception ex) { ex.printStackTrace(); return false; } }
用Java来Ping机器,有两个做法。一个是传统的调用命令行执行Ping命令的做法。这种做法的好处是速度快,比较可靠。缺点是,不同的操作系统,甚至Windows的不同版本,其执行和返回结果格式都可能不同,造成跨平台的不便以及代码的啰嗦。第二个方法自然就是使用大家都熟知的Java 5提供的InetAddress的isReachable方法。这个方法本来应当很好,可是在实际使用中就会发现,它不大灵光。超时时间设置短了吧,就ping不through;长了把,又贼慢。网上不少人都反映和抱怨这个问题。仔细研究这个isReachable,会发现更多的问题。1、它不是线程安全的。也就是说,为了提高速度而使用多线程进行多节点并行ping,会导致不安全的返回结果。这个问题挺致命。2、这个函数并非使用ICMP的ping,而是仅仅用TCP连一下7号端口而已:
写道
InetAddress.isReachable() doesn't use ICMP, it just tries to open TCP port 7 at the target. You can use ICMP in Java with the JPCAP library if you can find it, but you can't multithread it for reasons which are discussed above - basically, ICMP is not thread-safe
又慢又线程不安全就比较不爽了。还可以使用上面提到的JPCAP这个库来完成。这个库的地址是: http://netresearch.ics.uci.edu/kfujii/jpcap/doc/
不管怎么说,一个小小的ping还是挺麻烦。不过本例子由于仅仅是示例小程序,还是使用了isReachable方法,简化代码。
在ping通一个机器后,接下来再使用SNMP进行ping。做过SNMP网管的朋友知道,所谓SNMP Ping其实就是用SNMP去get一个非常基本的OID看对方有无反应。如果能够返回数据,说明这是一个SNMP节点,可以通过SNMP配合MIB库去获取更多的业务数据。例如磁盘、CPU、内存、端口力量等等基本的信息,都有相关的SNMP MIB进行定义。
这个例子使用了Westhawk's SNMP stack这个SNMP协议栈,一个轻量的、Java的、开源的、免费的SNMP协议栈,实现了SNMPv1、SNMPv2c以及SNMPv3 (包括MD5和SHA1以及DES, AES加密算法)。地址在这里:
使用Westhawk's SNMP做一个简单的get操作如下:
SnmpContextv2c context = new SnmpContextv2c(ip, 161); context.setCommunity("public"); BlockPdu pdu = new BlockPdu(context); pdu.setRetryIntervals(new int[]{1000}); String sysUpTime = "1.3.6.1.2.1.1.3.0"; pdu.addOid(sysUpTime); Object result = pdu.getResponseVariable(); 代码中用v2c,并假设community是public,超时时间1秒。获取sysUpTime也就是设备启动时间。如果有返回,认为节点存在且SNMP协议已启动。
本例子就ping这么多。如果做一个真正的综合设备网管,可以先获得设备的标识OID,判断其设备厂商和型号,然后加载对应设备支持的MIB进行复杂的监控。
多线程任务 由于一个网段需要ping的地址很多,一个线程会很慢。所以这个例子中使用很多线程并发进行。例如192.168.1.*里面有254个可能节点,就用10个线程去分头ping然后汇总。这个让人想起网络蚂蚁。于是就做了一个类似网络蚂蚁的界面。 其中,每个球是一个可能存在的节点地址。每个红色的球是一个线程正在ping这个节点。灰色的球是已经被ping过证明不存在或无法ping通的地址。绿色球是已经ping通,存在的节点。
通过调节线程的数量,可以掌握网络发现的速度。一般这254个节点,可以在30秒到60秒内完成。
拓扑呈现 拓扑呈现用TWaver就行了。每次发现一个存在的节点,往Network中new一个Node,设置一个图标即可。同时,在网段节点(一个云形图标的节点)和计算机节点创建一个连线。
同时,把拓扑图network组件的弹簧布局打开。这样,每次节点加入,都会像弹簧一样被自动布局到合适的位置,比较动感、有视觉效果。
network.getSpringLayouter().setMovableFilter(new MovableFilter() { public boolean isMovable(Element element) { return element != centerNode; } }); network.getSpringLayouter().start(); network.getSpringLayouter().setLinkRepulsionFactor(2);
另外,一旦ping通,我们在节点上就显示一个windows图标;如果snmp能ping通,再显示一个齿轮的图标。显示效果如下: 显示图标的代码很简单:
ResizableNode node = new ResizableNode(ipaddress); node.setImage("/demo/main/snmp/images/node.png"); node.addAttachment("winxp"); node.putAttachmentPosition(TWaverConst.POSITION_TOPLEFT); if (snmpPingOK) { node.addAttachment("snmp"); }
此外,可以通过windows的“net view hostname”的命令来查看一个机器的共享信息。我们做一个右键菜单,将执行命令结果显示出来:
显示结果如下:
结果显示,这台test计算机上有“move”、“SharedDocs”两个共享目录,以及三个共享打印机。实现的代码如下:
import java.io.*; import java.awt.*; import java.awt.event.*; import javax.swing.*; import twaver.*; import twaver.network.*; public class SnmpPopupMenuFactory implements PopupMenuFactory { private TNetwork network = null; public SnmpPopupMenuFactory(TNetwork network) { this.network = network; } public JPopupMenu getPopupMenu(DataBoxSelectionModel dataBoxSelectionModel, Point point) { if (network.getDataBox().getSelectionModel().size() == 1) { Element element = network.getDataBox().getSelectionModel().lastElement(); if (element instanceof ResizableNode) { final Node node = (Node) element; JPopupMenu menu = new JPopupMenu(); JMenuItem item = new JMenuItem("View this computer"); item.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { String result = executeCommand("net view \\\\" + node.getName()); if (result != null && !result.trim().isEmpty()) { JOptionPane.showMessageDialog(network, result); } else { JOptionPane.showMessageDialog(network, "No information available."); } } }); menu.add(item); return menu; } } return null; } private static String executeCommand(String command) { try { Process p = Runtime.getRuntime().exec(command); InputStreamReader ir = new InputStreamReader(p.getInputStream()); LineNumberReader input = new LineNumberReader(ir); String result = null; String line = input.readLine(); while (line != null) { if (result == null) { result = line; } else { if (!line.trim().equalsIgnoreCase("")) { result = result + "\n" + line.trim(); } } line = input.readLine(); } return result; } catch (IOException ex) { ex.printStackTrace(); } return null; } //可以用这两行代码来测试test机器的返回结果。 public static void main(String[] args) { String result = executeCommand("net view \\\\test"); System.out.println(result); } }
链路探测与告警 在所有的节点被探索结束并放入界面后,我们可以起一个线程,周期性对每个节点进行ping。一旦无法ping通,生成告警,显示在拓扑图中。
Thread linkCheckThread = new Thread() { @Override public void run() { while (true) { try { Thread.sleep(3000); if (!network.getDataBox().isEmpty()) { Collection elements = network.getDataBox().getAllElements(); Iterator it = elements.iterator(); while (it.hasNext()) { final Element element = (Element) it.next(); if (element instanceof ResizableNode) { final String ipaddress = element.getID().toString(); final boolean pingOK = ping(ipaddress); SwingUtilities.invokeLater(new Runnable() { public void run() { Alarm alarm = new Alarm(); if (!pingOK) { alarm.setAlarmSeverity(AlarmSeverity.CRITICAL); } else { if (element.getAlarmState().isEmpty()) { return; } alarm.setAlarmSeverity(AlarmSeverity.CLEARED); } alarm.setElementID(ipaddress); alarm.setProbableCause(AlarmProbableCause.LINE_INTERFACE_FAILURE); box.getAlarmModel().addAlarm(alarm); } }); } } } } catch (InterruptedException ex) { ex.printStackTrace(); } } } }; linkCheckThread.start(); }
将告警放置在一个告警表格中:
最终效果以及源代码下载 这是用这个小程序探索我们办公室的网络结构。你的呢?也可以发上来看看!
GOOD LUCK & HAVE FUN!
声明:ITeye文章版权属于作者,受法律保护。没有作者书面许可不得转载。
推荐链接
|
|
返回顶楼 | |
发表时间:2010-08-26
为何就看到一台机器(自己) 难道其余的装了防火墙就看不到了?
|
|
返回顶楼 | |
发表时间:2010-08-26
这是Twaver的例子
|
|
返回顶楼 | |
发表时间:2010-08-27
snake1987 写道 拓扑没我们公司的好看, 楼主,这可不是你的水平哦~ 哈哈,我压力好大啊! |
|
返回顶楼 | |
发表时间:2010-08-27
貌似很多机器检测不到?
|
|
返回顶楼 | |
发表时间:2010-08-27
高人呀!以前开发从来没有涉及到网络和多线程这块!先下载下来,学习学习!多谢分享!
|
|
返回顶楼 | |
发表时间:2010-08-27
程序经常假死,然后直接蓝屏
运行两次,蓝屏两次了 |
|
返回顶楼 | |
发表时间:2010-08-27
snake1987 写道 拓扑没我们公司的好看,
楼主,这可不是你的水平哦~ LZ也就举个个demo,要好看全靠美工 拓扑没我们公司的好看 (当然我们公司请了专业设计公司操刀界面) |
|
返回顶楼 | |
发表时间:2010-08-27
晨星★~雨泪 写道 貌似很多机器检测不到? 嗯,java的Ping确实不是很好用。可以试试这种ping,看是否好些(我也没用过): Socket t = new Socket(args[0], 7); DataInputStream dis = new DataInputStream(t.getInputStream()); PrintStream ps = new PrintStream(t.getOutputStream()); ps.println("Hello"); String str = is.readLine(); if (str.equals("Hello")) return true; else return false; t.close(); |
|
返回顶楼 | |
发表时间:2010-08-27
Swing做出来的东西还是挺漂亮的嘛,呵呵!
学习一下! |
|
返回顶楼 | |