• 解决InetAddress.isReachable(timeout)在windows xp始终返回false的bug


    笔者最近在做产品,其中一个环节用到ping测试主机是否在线。

    开发环境:Windows 7 64bit+JDK1.8 x64

    以下是检测主机是否在线,开发环境中测试通过

    public static boolean hostAvailabilityCheck(String host,int timeout){        
            try {                        
                InetAddress inet = InetAddress.getByName(host);
                System.out.println("Sending Ping Request to " + inet);
                if(inet.isReachable(timeout)){                
                    System.out.println("Host is reachable");                
                    return true;
                }
            } catch (IOException e) {
                System.out.println("Host is NOT reachable");
                e.printStackTrace();
            }
            return false;
        }

    正常运行结果

    当在windows XP下就运行不正常了,inet.isReachable处始终返回false。Google了一番(java inetaddress isreachable windows xp did not work),

    确定这是个bug,有可能是因为Microsoft放弃了Windows XP等不再维护的操作系统,Oracle公司也不再向后兼容这些系统了。网上反馈bug的比比皆是。

    JDK-5061568 : java.net.InetAddress.isReachable() kills Windows networking文章说这个bug会一直重现(This bug can be reproduced always.)

    JDK-5061571 : InetAddress#isReachable does not send PINGs but only TCP echos

    JDK-6595834 : InetAddress.isReachable is not thread safe when using ICMP ECHO.

    以上三篇文章就指出了这个方法存在的一些问题:非线程安全、只发送TCP ECHO,对于框架设计人员来说,作为客户端编程人员我们关注的是api是否

    能够达到预期目的,可是jdk1.8.0_144仍旧没有解决这个问题。

    笔者最终通过网络搜索,在stackoverflow上找到了解决方法。

    解决方法一(推荐):

    出处:Why does InetAddress.isReachable return false, when I can ping the IP address?

    // in case of Linux change the 'n' to 'c' 如果是Linux系统,请将n改成c
        Process p1 = java.lang.Runtime.getRuntime().exec("ping -n 1 www.google.com");
        int returnVal = p1.waitFor();
        boolean reachable = (returnVal==0);

    windows系统批处理返回值非0即失败。所以,如果以上返回为0说明ping的通,如果返回2则失败。

    解决方法二:

    出处:Odd InetAddress.isReachable() issue

    说明:需要第三方库JNA支持,需要一定的操作系统知识。此方法将会通过JNA调用COM组件来调用Windows API函数,

    相关DLL是IPHLPAPI.DLL和 WSOCK32.DLL

    public interface InetAddr extends StdCallLibrary {
        InetAddr INSTANCE = (InetAddr) 
                Native.loadLibrary("wsock32.dll", InetAddr.class);
    
        ULONG inet_addr(String cp);                     //in_addr creator. Creates the in_addr C struct used below
    }
    
    public interface IcmpEcho extends StdCallLibrary {
        IcmpEcho INSTANCE = (IcmpEcho)
                Native.loadLibrary("iphlpapi.dll", IcmpEcho.class);
    
        int IcmpSendEcho(
                HANDLE IcmpHandle,                      //Handle to the ICMP
                ULONG DestinationAddress,               //Destination address, in the form of an in_addr C Struct defaulted to ULONG
                Pointer RequestData,                    //Pointer to the buffer where my Message to be sent is
                short RequestSize,                      //size of the above buffer. sizeof(Message)
                byte[] RequestOptions,                  //OPTIONAL!! Can set this to NULL
                Pointer ReplyBuffer,                    //Pointer to the buffer where the replied echo is written to
                int ReplySize,                          //size of the above buffer. Normally its set to the sizeof(ICMP_ECHO_REPLY), but arbitrarily set it to 256 bytes
                int Timeout);                           //time, as int, for timeout
    
        HANDLE IcmpCreateFile();                        //win32 ICMP Handle creator
    
        boolean IcmpCloseHandle(HANDLE IcmpHandle);     //win32 ICMP Handle destroyer
    }
    public void SendReply(String ipAddress) {
        final IcmpEcho icmpecho = IcmpEcho.INSTANCE;
        final InetAddr inetAddr = InetAddr.INSTANCE;
        HANDLE icmpHandle = icmpecho.IcmpCreateFile();
        byte[] message = new String("thisIsMyMessage!".toCharArray()).getBytes();
        Memory messageData = new Memory(32);                    //In C/C++ this would be: void *messageData = (void*) malloc(message.length);
        messageData.write(0, message, 0, message.length);       //but ignored the length and set it to 32 bytes instead for now
        Pointer requestData = messageData;
        Pointer replyBuffer = new Memory(256);
        replyBuffer.clear(256);
    
        // HERE IS THE NATIVE CALL!!
        reply = icmpecho.IcmpSendEcho(icmpHandle, 
                inetAddr.inet_addr(ipAddress), 
                requestData, 
                (short) 32, 
                null, 
                replyBuffer, 
                256, 
                timeout);
        // NATIVE CALL DONE, CHECK REPLY!!
    
        icmpecho.IcmpCloseHandle(icmpHandle);
    }
    
    public boolean IsReachable () {
        return (reply > 0);
    }

    笔者简单封装了一下代码

    class IcmpEchoPatch{        
        int reply;
        public void SendReply(String host,int timeout) {
            final IcmpEcho icmpecho = IcmpEcho.INSTANCE;
            final InetAddr inetAddr = InetAddr.INSTANCE;
            HANDLE icmpHandle = icmpecho.IcmpCreateFile();
            byte[] message = new String("thisIsMyMessage!".toCharArray()).getBytes();
            Memory messageData = new Memory(32);                    //In C/C++ this would be: void *messageData = (void*) malloc(message.length);
            messageData.write(0, message, 0, message.length);       //but ignored the length and set it to 32 bytes instead for now
            Pointer requestData = messageData;
            Pointer replyBuffer = new Memory(256);
            replyBuffer.clear(256);
    
            // HERE IS THE NATIVE CALL!!
            reply = icmpecho.IcmpSendEcho(icmpHandle, 
                    inetAddr.inet_addr(host), 
                    requestData, 
                    (short) 32, 
                    null, 
                    replyBuffer, 
                    256, 
                    timeout);
            // NATIVE CALL DONE, CHECK REPLY!!
            icmpecho.IcmpCloseHandle(icmpHandle);
        }
    
        public boolean IsReachable () {
            return (reply > 0);
        }
    }

     最终hostAvailabilityCheck方法代码如下:

    public static boolean hostAvailabilityCheck(String host,int timeout){        
            try {
                
                HardWareIdentifier resolver = HardWareIdentifier.getDefault(); //替换为你的方法
                if(resolver.isMinSupportPlat()){ //替换为你的方法判断操作系统
                    IcmpEchoPatch ping = new IcmpEchoPatch();
                    ping.SendReply(host, timeout);
                    if(ping.IsReachable()){
                        System.out.println("Host is reachable");                
                        return true;
                    }
                }else{
                    InetAddress inet = InetAddress.getByName(host);
                    System.out.println("Sending Ping Request to " + inet);
                    if(inet.isReachable(timeout)){                
                        System.out.println("Host is reachable");                
                        return true;
                    }
                }
            } catch (IOException e) {
                System.out.println("Host is NOT reachable");
                e.printStackTrace();
            }
            return false;
        }
  • 相关阅读:
    CF733F
    P4826
    洛谷P2687 & P1108
    CF42A
    洛谷P1858
    CF1428C
    洛谷P4981
    树形DP
    背包六讲(也不知道为啥就是六个 $QwQ$)
    2020
  • 原文地址:https://www.cnblogs.com/passedbylove/p/7561006.html
Copyright © 2020-2023  润新知