一般情况下,我们仅仅需要判断从一台机器是否可以访问(ping)到另一台机器,此时,可以简单的使用 java 类库中 java.net.inetaddress 类来实现,这个类提供了两个方法探测远程机器是否可达
boolean isreachable(int timeout) // 测试地址是否可达 boolean isreachable(networkinterface netif, int ttl, int timeout) // 测试地址是否可达.
简单说来,上述方法就是通过远端机器的 ip 地址构造 inetaddress 对象,然后调用其 isreachable 方法,测试调用机器和远端机器的网络可达性。注意到远端机器可能有多个 ip 地址,因而可能要迭代的测试所有的情况。
void isaddressavailable(string ip){ try{ inetaddress address = inetaddress.getbyname(ip);//ping this ip if(address instanceof java.net.inet4address){ system.out.println(ip + is ipv4 address); }else if(address instanceof java.net.inet6address){ system.out.println(ip + is ipv6 address); }else{ system.out.println(ip + is unrecongized); } if(address.isreachable(5000)){ system.out.println(success - ping + ip + with no interface specified); }else{ system.out.println(failure - ping + ip + with no interface specified); } system.out.println(\n-------trying different interfaces--------\n); enumeration<networkinterface> netinterfaces = networkinterface.getnetworkinterfaces(); while(netinterfaces.hasmoreelements()) { networkinterface ni = netinterfaces.nextelement(); system.out.println( checking interface, displayname: + ni.getdisplayname() + , name: + ni.getname()); if(address.isreachable(ni, 0, 5000)){ system.out.println(success - ping + ip); }else{ system.out.println(failure - ping + ip); } enumeration<inetaddress> ips = ni.getinetaddresses(); while(ips.hasmoreelements()) { system.out.println(ip: + ips.nextelement().gethostaddress()); } system.out.println(-------------------------------------------); } }catch(exception e){ system.out.println(error occurs.); e.printstacktrace(); } }
程序输出:
--------------start-------------- 10.13.20.70 is ipv4 address success - ping 10.13.20.70 with no interface specified -------trying different interfaces-------- checking interface, displayname:ms tcp loopback interface, name:lo failure - ping 10.13.20.70 ip: 127.0.0.1 ------------------------------------------- checking interface, displayname:intel(r) centrino(r) advanced-n 6200 agn - teefer2 miniport, name:eth0 failure - ping 10.13.20.70 ip: 9.123.231.40 ------------------------------------------- checking interface, displayname:intel(r) 82577lm gigabit network connection - teefer2 miniport, name:eth2 success - ping 10.13.20.70 ------------------------------------------- checking interface, displayname:wan (ppp/slip) interface, name:ppp0 success - ping 10.13.20.70 ip: 10.0.50.189 ------------------------------------------- --------------end--------------
从上可以看出 isreachable 的用法,可以不指定任何接口来判断远端网络的可达性,但这不能区分出数据包是从那个网络接口发出去的 ( 如果本地有多个网络接口的话 );而高级版本的 isreachable 则可以指定从本地的哪个网络接口测试,这样可以准确的知道远端网络可以连通本地的哪个网络接口。
但是,java 本身没有提供任何方法来判断本地的哪个 ip 地址可以连通远端网络,java 网络编程接口也没有提供方法来访问 icmp 协议数据包,因而通过 icmp 的网络不可达数据包实现这一点也是不可能的 ( 当然可以用 jni 来实现,但就和系统平台相关了 ), 此时可以考虑本文下一节提出的方法。
在某些情况下,我们可能要确定本地的哪个网络地址可以连通远程网络,以便远程网络可以回连到本地使用某些服务或发出某些通知。一个典型的应用场景 是,本地启动了文件传输服务 ( 如 ftp),需要将本地的某个 ip 地址发送到远端机器,以便远端机器可以通过该地址下载文件;或者远端机器提供某些服务,在某些事件发生时通知注册了获取这些事件的机器 ( 常见于系统管理领域 ),因而在注册时需要提供本地的某个可达 ( 从远端 ) 地址。
虽然我们可以用 inetaddress.isreachabl 方法判断出本地的哪个网络接口可连通远程玩过,但是由于单个网络接口是可以配置多个 ip 地址的,因而在此并不合适。我们可以使用 socket 建立可能的 tcp 连接,进而判断某个本地 ip 地址是否可达远程网络。我们使用 java.net.socket 类中的 connect 方法
void connect(socketaddress endpoint, int timeout) //使用socket连接服务器,指定超时的时间
这种方法需要远程的某个端口,该端口可以是任何基于 tcp 协议的开放服务的端口(如一般都会开放的 echo 服务端口 7, linux 的 ssh 服务端口 22 等)。实际上,建立的 tcp 连接被协议栈放置在连接队列,进而分发到真正处理数据的各个应用服务,由于 udp 没有连接的过程,因而基于 udp 的服务(如 snmp)无法在此方法中应用。
具体过程是,枚举本地的每个网络地址,建立本地 socket,在某个端口上尝试连接远程地址,如果可以连接上,则说明该本地地址可达远程网络。
void printreachableip(inetaddress remoteaddr, int port){ string retip = null; enumeration<networkinterface> netinterfaces; try{ netinterfaces = networkinterface.getnetworkinterfaces(); while(netinterfaces.hasmoreelements()) { networkinterface ni = netinterfaces.nextelement(); enumeration<inetaddress> localaddrs = ni.getinetaddresses(); while(localaddrs.hasmoreelements()){ inetaddress localaddr = localaddrs.nextelement(); if(isreachable(localaddr, remoteaddr, port, 5000)){ retip = localaddr.gethostaddress(); break; } } } } catch(socketexception e) { system.out.println( error occurred while listing all the local network addresses.); } if(retip == null){ system.out.println(null reachable local ip is found!); }else{ system.out.println(reachable local ip is found, it is + retip); } } boolean isreachable(inetaddress localinetaddr, inetaddress remoteinetaddr, int port, int timeout) { booleanisreachable = false; socket socket = null; try{ socket = newsocket(); // 端口号设置为 0 表示在本地挑选一个可用端口进行连接 socketaddress localsocketaddr = new inetsocketaddress(localinetaddr, 0); socket.bind(localsocketaddr); inetsocketaddress endpointsocketaddr = new inetsocketaddress(remoteinetaddr, port); socket.connect(endpointsocketaddr, timeout); system.out.println(success - connection established! local: + localinetaddr.gethostaddress() + remote: + remoteinetaddr.gethostaddress() + port + port); isreachable = true; } catch(ioexception e) { system.out.println(failre - can not connect! local: + localinetaddr.gethostaddress() + remote: + remoteinetaddr.gethostaddress() + port + port); } finally{ if(socket != null) { try{ socket.close(); } catch(ioexception e) { system.out.println(error occurred while closing socket..); } } } return isreachable; }
运行结果
--------------start-------------- failre - can not connect! local: 127.0.0.1 remote: 10.8.1.50 port22 failre - can not connect! local: 9.123.231.40 remote: 10.8.1.50 port22 success - connection established! local: 10.0.50.189 remote: 10.8.1.50 port22 reachable local ip is found, it is 10.0.50.189 --------------end--------------
当网络环境中存在 ipv4 和 ipv6,即机器既有 ipv4 地址,又有 ipv6 地址的时候,我们可以对程序进行一些优化,比如
由于 ipv4 和 ipv6 地址之间是无法互相访问的,因此仅需要判断 ipv4 地址之间和 ipv6 地址之间的可达性。
对于 ipv4 的换回地址可以不做判断,对于 ipv6 的 linklocal 地址也可以跳过测试
根据实际的需要,我们可以优先考虑选择使用 ipv4 或者 ipv6,提高判断的效率
判断本地地址和远程地址是否同为 ipv4 或者 ipv6
// 判断是 ipv4 还是 ipv6 if(!((localinetaddr instanceofinet4address) && (remoteinetaddr instanceofinet4address) || (localinetaddr instanceofinet6address) && (remoteinetaddr instanceofinet6address))){ // 本地和远程不是同时是 ipv4 或者 ipv6,跳过这种情况,不作检测 break; }
跳过本地地址和 linklocal 地址
if( localaddr.isloopbackaddress() || localaddr.isanylocaladdress() || localaddr.islinklocaladdress() ){ // 地址为本地环回地址,跳过 break; }
以上就是java网络连通性如何测试的详细内容。
