笔者最近在做产品,其中一个环节用到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; }
创作文章不容易,转载文章必须注明文章出处;如果这篇文章对您有帮助,点击右侧打赏,支持一下吧。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· 10年+ .NET Coder 心语 ── 封装的思维:从隐藏、稳定开始理解其本质意义
· 地球OL攻略 —— 某应届生求职总结
· 提示词工程——AI应用必不可少的技术
· Open-Sora 2.0 重磅开源!
· 周边上新:园子的第一款马克杯温暖上架