论坛首页 Java企业应用论坛

Swing版小小网管

浏览 19908 次
该帖已经被评为精华帖
作者 正文
   发表时间: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加密算法)。地址在这里:

http://snmp.westhawk.co.uk/

 

使用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();
    }

 

 将告警放置在一个告警表格中:



 同时,让告警表和拓扑图共享一个DataBox,于是告警就会在拓扑中显示:

 


 

最终效果以及源代码下载

这是用这个小程序探索我们办公室的网络结构。你的呢?也可以发上来看看!

 




源代码、第三方lib包、可执行包、run.bat都在附件中,请大家自行下载。请确保安装了JAVA 6。解压后双击run.bat即可。在弹出的对话框中点击start按钮即可进行网络自动发现。

 

源代码和可执行文件点击下载

 

GOOD LUCK & HAVE FUN!

 

 

  • 大小: 21.9 KB
  • 大小: 14.5 KB
  • 大小: 3.7 KB
  • 大小: 36.6 KB
  • 大小: 105.1 KB
  • 大小: 13.2 KB
  • 大小: 4.9 KB
  • 大小: 18.4 KB
  • 大小: 87.9 KB
   发表时间:2010-08-26  
为何就看到一台机器(自己) 难道其余的装了防火墙就看不到了?
0 请登录后投票
   发表时间:2010-08-26  
这是Twaver的例子
0 请登录后投票
   发表时间:2010-08-27  
snake1987 写道
拓扑没我们公司的好看,
楼主,这可不是你的水平哦~


哈哈,我压力好大啊!
0 请登录后投票
   发表时间:2010-08-27  
貌似很多机器检测不到?
0 请登录后投票
   发表时间:2010-08-27  
高人呀!以前开发从来没有涉及到网络和多线程这块!先下载下来,学习学习!多谢分享!
0 请登录后投票
   发表时间:2010-08-27  
程序经常假死,然后直接蓝屏

运行两次,蓝屏两次了
0 请登录后投票
   发表时间:2010-08-27  
snake1987 写道
拓扑没我们公司的好看,
楼主,这可不是你的水平哦~




LZ也就举个个demo,要好看全靠美工

拓扑没我们公司的好看   (当然我们公司请了专业设计公司操刀界面)
0 请登录后投票
   发表时间: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();
0 请登录后投票
   发表时间:2010-08-27  
Swing做出来的东西还是挺漂亮的嘛,呵呵!
学习一下!
0 请登录后投票
论坛首页 Java企业应用版

跳转论坛:
Global site tag (gtag.js) - Google Analytics