เหตุใด InetAddress.isReachable จึงส่งคืนเท็จเมื่อฉันสามารถ ping ที่อยู่ IP ได้


98
InetAddress byName = InetAddress.getByName("173.39.161.140");
System.out.println(byName);
System.out.println(byName.isReachable(1000));

จะisReachableกลับมาทำไมfalse? ฉันสามารถ ping IP


อาจซ้ำกันได้: stackoverflow.com/questions/4779367/…
assylias

1
มันคล้ายกัน แต่ฉันไม่พบเบาะแสใด ๆ ในการแก้ปัญหาดังนั้นฉันจึงให้คะแนนที่นี่ ขอบคุณสำหรับการแจ้งเตือน!
jiafu

2
ฉันจะลองเพิ่มระยะหมดเวลา
jayunit100

นี่เป็นคำถามที่ดีมากมีคะแนนโหวตไม่เพียงพอ คำถามที่คล้ายกันเดียวที่ฉันพบถูกแท็กเป็นคำถามที่ไม่ชัดเจนและคำตอบนั้นสรุปไม่ได้
jayunit100

3
ใครช่วยบอกทีว่า Reachable () คืออะไร? มันส่งคืนฉันเป็นเท็จแม้ใน localhost ...
Seth Keno

คำตอบ:


73

วิธี"isReachable"ไม่สมควรใช้สำหรับฉันในหลาย ๆ กรณี คุณสามารถเลื่อนลงไปด้านล่างเพื่อดูทางเลือกของฉันสำหรับการทดสอบว่าคุณออนไลน์และสามารถแก้ไขโฮสต์ภายนอกได้ (เช่น google.com) ... ซึ่งโดยทั่วไปดูเหมือนว่าจะทำงานบนเครื่อง * NIX

ปัญหา

มีการพูดพล่อยมากมายเกี่ยวกับเรื่องนี้:

ส่วนที่ 1: ตัวอย่างที่ทำซ้ำได้ของปัญหา

โปรดทราบว่าในกรณีนี้ล้มเหลว

       //also, this fails for an invalid address, like "www.sjdosgoogle.com1234sd" 
       InetAddress[] addresses = InetAddress.getAllByName("www.google.com");
      for (InetAddress address : addresses) {
        if (address.isReachable(10000))
        {   
           System.out.println("Connected "+ address);
        }
        else
        {
           System.out.println("Failed "+address);
        }
      }
          //output:*Failed www.google.com/74.125.227.114*

ส่วนที่ 2: วิธีแก้ปัญหาแฮ็ก

คุณสามารถทำได้โดยทำดังนี้

// in case of Linux change the 'n' to 'c'
    Process p1 = java.lang.Runtime.getRuntime().exec("ping -n 1 www.google.com");
    int returnVal = p1.waitFor();
    boolean reachable = (returnVal==0);

ตัวเลือก -c ของpingจะช่วยให้ ping พยายามเข้าถึงเซิร์ฟเวอร์เพียงครั้งเดียว (ตรงข้ามกับ ping ที่ไม่มีที่สิ้นสุดซึ่งเราคุ้นเคยกับการใช้ที่เทอร์มินัล)

นี้จะกลับ0 ถ้าโฮสต์สามารถเข้าถึงได้ มิฉะนั้นคุณจะได้รับ "2" เป็นค่าตอบแทน

ง่ายกว่ามาก - แต่แน่นอนว่าเป็นแพลตฟอร์มเฉพาะ และอาจมีข้อควรระวังเกี่ยวกับสิทธิพิเศษบางประการในการใช้คำสั่งนี้ - แต่ฉันพบว่ามันใช้ได้กับเครื่องของฉัน


โปรดทราบว่า: 1) การแก้ปัญหานี้ไม่ใช่คุณภาพการผลิต มันเป็นการแฮ็กเล็กน้อย หาก Google หยุดทำงานหรืออินเทอร์เน็ตของคุณช้าชั่วคราวหรืออาจถึงแม้จะมีความสนุกสนานในการตั้งค่าสิทธิ์ / ระบบของคุณหากสามารถส่งคืนค่าลบเท็จ (กล่าวคืออาจล้มเหลวแม้ว่าที่อยู่ที่ป้อนจะเข้าถึงได้) 2) ความล้มเหลว isReachable เป็นปัญหาที่โดดเด่น อีกครั้ง - มีแหล่งข้อมูลออนไลน์มากมายที่ระบุว่าไม่มีวิธีที่ "สมบูรณ์แบบ" ในการดำเนินการนี้ในขณะที่เขียนนี้เนื่องจากวิธีที่ JVM พยายามเข้าถึงโฮสต์ - ฉันเดาว่ามันเป็นงานเฉพาะของแพลตฟอร์มที่อยู่ภายในซึ่งแม้ว่าจะเรียบง่าย ยังไม่ได้รับการสรุปอย่างเพียงพอโดย JVM


@ jayunit100 สำหรับทั้งสองวิธีไม่ได้ผลของisReachableฉันล้มเหลวและใช้ ping ฉันได้รับ icmp ไม่ได้รับอนุญาต? คุณรู้วิธีจัดการกับมันตอนนี้หรือไม่?
Yuvi

6
@Yuvi หากคุณใช้ Windows แฟล็กจะแตกต่างกัน แทนที่จะเป็น -c คุณต้องการ -n
James T Snell

waitFor () ใช้เวลา 10 วินาทีในการย้อนกลับ มีวิธีพูดถึงการหมดเวลาใน Java 7 หรือไม่?
Jaydev

@Jaydev มีตัวเลือกมากเกินไป: public boolean waitFor(long timeout, TimeUnit unit)ใน java.lang.Process (@since 1.8)
แบ่งแยกไม่ได้

ตั้งแต่วันที่ 2020-01 ฉันสามารถเรียกใช้ตัวอย่างของปัญหาได้โดยไม่มีปัญหาใด ๆ รายงาน "เชื่อมต่อแล้ว" สำหรับหนึ่ง IPv4 และหนึ่งที่อยู่ IPv6 ข้อผิดพลาดนอกจากนี้ยังมีการแก้ไขใน Java 5.0 B22 อาจจะisReachableมีความน่าเชื่อถือมากขึ้นในปัจจุบัน
Lii

56

ฉันมาที่นี่เพื่อรับคำตอบสำหรับคำถามเดียวกันนี้ แต่ฉันไม่พอใจกับคำตอบใด ๆ เพราะฉันกำลังมองหาโซลูชันที่เป็นอิสระจากแพลตฟอร์ม นี่คือรหัสที่ฉันเขียนและเป็นแพลตฟอร์มที่ไม่ขึ้นกับแพลตฟอร์ม แต่ต้องการข้อมูลเกี่ยวกับพอร์ตที่เปิดบนเครื่องอื่น (ซึ่งเรามีอยู่เกือบตลอดเวลา)

private static boolean isReachable(String addr, int openPort, int timeOutMillis) {
    // Any Open port on other machine
    // openPort =  22 - ssh, 80 or 443 - webserver, 25 - mailserver etc.
    try {
        try (Socket soc = new Socket()) {
            soc.connect(new InetSocketAddress(addr, openPort), timeOutMillis);
        }
        return true;
    } catch (IOException ex) {
        return false;
    }
}

2
นี่เป็นโซลูชันที่เป็นอิสระสำหรับแพลตฟอร์มที่ยอดเยี่ยมขอขอบคุณ!
ivandov

1
ฉันมีความสุขที่ได้มาที่นี่วิธีที่ดีในการคิด Sourabh :)
JustADev

ดังนั้นเพื่อให้แน่ใจว่าฉันเข้าใจอย่างถ่องแท้: ตราบใดที่ไม่มีข้อยกเว้นสามารถเข้าถึงที่อยู่ได้?
Timmiej93

1
สิ่งนี้เหมือนกับสิ่งที่ทำInetAddress.isReachable()อยู่แล้วผ่านพอร์ต 7 ยกเว้นว่าตัวหลังฉลาดกว่าเกี่ยวกับความIOExceptionsหมายที่เป็นไปได้ต่างๆในแง่ของความสามารถในการเข้าถึง
Marquis of Lorne

1
ใช่ @EJP ฉันคิดว่ามันจะดีมากถ้าInetAddress.isReachable()มีการใส่อาร์กิวเมนต์พอร์ตมากเกินไปในไลบรารีมาตรฐาน ฉันสงสัยว่าทำไมมันไม่รวม?
Sourabh Bhat

7

หากคุณต้องการตรวจสอบว่าเชื่อมต่อกับอินเทอร์เน็ตหรือไม่ให้ใช้วิธีนี้มันจะคืนค่าจริงหากเชื่อมต่ออินเทอร์เน็ตจะดีกว่าถ้าคุณใช้ที่อยู่ของไซต์ที่คุณพยายามเชื่อมต่อผ่านโปรแกรม

     public static boolean isInternetReachable()
    {
        try {
            //make a URL to a known source
            URL url = new URL("http://www.google.com");

            //open a connection to that source
            HttpURLConnection urlConnect = (HttpURLConnection)url.openConnection();

            //trying to retrieve data from the source. If there
            //is no connection, this line will fail
            Object objData = urlConnect.getContent();

        } catch (Exception e) {              
            e.printStackTrace();
            return false;
        }

        return true;
    }

3
ไม่มีประโยชน์ถ้าเซิร์ฟเวอร์ไม่มีเว็บเซิร์ฟเวอร์นั่นไม่ใช่เงื่อนไขที่ระบุไว้ในคำถามเลย
Fran Marzoa

4

เพียงแค่พูดอย่างชัดเจนเนื่องจากคำตอบอื่น ๆ ไม่มี ส่วน ping ของ isReachable () ต้องการการเข้าถึงรูทบน Unix และตามที่ bestsss ชี้ให้เห็นใน4779367 :

และถ้าคุณถามว่าทำไม ping จาก bash ไม่ได้จริงๆแล้วมันก็ต้องการเช่นกัน ทำเช่นนั้น ls -l / bin / ping

เนื่องจากการใช้ root ไม่ใช่ตัวเลือกในกรณีของฉันวิธีแก้ปัญหาคืออนุญาตให้เข้าถึงพอร์ต 7 ในไฟร์วอลล์ไปยังเซิร์ฟเวอร์เฉพาะที่ฉันสนใจ


4

ฉันไม่แน่ใจว่าสถานะเป็นอย่างไรเมื่อคำถามเดิมถูกถามกลับในปี 2555

ในขณะนี้ ping จะดำเนินการในฐานะรูท ผ่านการอนุญาตของ ping ที่เรียกใช้งานได้คุณจะเห็นแฟล็ก + s และกระบวนการที่เป็นของ root ซึ่งหมายความว่ามันจะทำงานเป็น root เรียกใช้ ls -liat ที่ตำแหน่ง ping และคุณจะเห็น

ดังนั้นหากคุณเรียกใช้ InetAddress.getByName ("www.google.com") isReacheable (5000) เป็นรูทก็ควรคืนค่าจริง

คุณต้องมีการอนุญาตที่เหมาะสมสำหรับซ็อกเก็ตดิบซึ่ง ICMP ใช้ (โปรโตคอลที่ใช้โดย ping)

InetAddress.getByName มีความน่าเชื่อถือพอ ๆ กับ ping แต่คุณต้องมีสิทธิ์ที่เหมาะสมในกระบวนการเพื่อให้มันทำงานได้อย่างถูกต้อง


0

เนื่องจากคุณสามารถ ping คอมพิวเตอร์ได้กระบวนการ Java ของคุณควรทำงานโดยมีสิทธิ์เพียงพอที่จะทำการตรวจสอบ น่าจะเกิดจากการใช้พอร์ตในช่วงล่าง หากคุณรันโปรแกรม java ด้วย sudo / superuser ฉันจะพนันได้เลยว่ามันใช้ได้


คุณไม่จำเป็นต้องมีสิทธิ์ในการเชื่อมต่อกับพอร์ตในช่วงต่ำ
Marquis of Lorne

0

ฉันขอแนะนำว่าวิธีเดียวที่เชื่อถือได้ในการทดสอบการเชื่อมต่ออินเทอร์เน็ตคือการเชื่อมต่อและดาวน์โหลดไฟล์จริง ๆ หรือเพื่อแยกวิเคราะห์ผลลัพธ์ของการโทร ping ของระบบปฏิบัติการผ่าน exec () คุณไม่สามารถพึ่งพารหัสทางออกสำหรับ ping และ isReachable () คืออึ

คุณไม่สามารถพึ่งพารหัสออกจาก ping ได้เนื่องจากจะส่งกลับ 0 หากคำสั่ง ping ดำเนินการอย่างถูกต้อง น่าเสียดายที่ ping ดำเนินการอย่างถูกต้องหากไม่สามารถเข้าถึงโฮสต์เป้าหมาย แต่ได้รับ "โฮสต์ปลายทางที่ไม่สามารถเข้าถึงได้" จากเราเตอร์ ADSL ที่บ้านของคุณ นี่เป็นการตอบกลับที่ถือว่าเป็น Hit ที่สำเร็จดังนั้น exit code = 0 ต้องเพิ่มแม้ว่านี่จะอยู่ในระบบ Windows ไม่ได้ตรวจสอบ * nixes


0
 private boolean isReachable(int nping, int wping, String ipping) throws Exception {

    int nReceived = 0;
    int nLost = 0;

    Runtime runtime = Runtime.getRuntime();
    Process process = runtime.exec("ping -n " + nping + " -w " + wping + " " + ipping);
    Scanner scanner = new Scanner(process.getInputStream());
    process.waitFor();
    ArrayList<String> strings = new ArrayList<>();
    String data = "";
    //
    while (scanner.hasNextLine()) {
        String string = scanner.nextLine();
        data = data + string + "\n";
        strings.add(string);
    }

    if (data.contains("IP address must be specified.")
            || (data.contains("Ping request could not find host " + ipping + ".")
            || data.contains("Please check the name and try again."))) {
        throw new Exception(data);
    } else if (nping > strings.size()) {
        throw new Exception(data);
    }

    int index = 2;

    for (int i = index; i < nping + index; i++) {
        String string = strings.get(i);
        if (string.contains("Destination host unreachable.")) {
            nLost++;
        } else if (string.contains("Request timed out.")) {
            nLost++;
        } else if (string.contains("bytes") && string.contains("time") && string.contains("TTL")) {
            nReceived++;
        } else {
        }
    }

    return nReceived > 0;
}

nping คือจำนวนการพยายาม ping ip (แพ็กเก็ต) หากคุณมีเครือข่ายหรือระบบไม่ว่างให้เลือกหมายเลข nping ที่ใหญ่กว่า
wping เป็นเวลารอ pong จาก ip คุณสามารถตั้งค่าได้ 2000ms
สำหรับการใช้วิธีนี้คุณสามารถเขียนสิ่งนี้:

isReachable(5, 2000, "192.168.7.93");

การแทรกคำสั่งที่เป็นไปได้โปรดตรวจสอบความถูกต้องของอินพุตสตริงก่อนที่จะรัน
สอดแนม

นอกจากนี้ยังมีข้อความแสดงข้อผิดพลาด "ความล้มเหลวทั่วไป" นอกจากนี้ยังดูเหมือนเฉพาะหน้าต่าง ฉันค่อนข้างแน่ใจว่าข้อความแสดงข้อผิดพลาดเหล่านี้ได้รับการแปลเป็นภาษาต่างๆดังนั้นจึงไม่ใช่เรื่องพกพา
สอดแนม

สำหรับระบบ windows
Armin Mokri

ใช่ฉันเคยเห็นมาก่อนบน windows
spy

0

หรือใช้วิธีนี้:

public static boolean exists(final String host)
{
   try
   {
      InetAddress.getByName(host);
      return true;
   }
   catch (final UnknownHostException exception)
   {
      exception.printStackTrace();
      // Handler
   }
   return false;
}

0

InetAddress.isReachable เครื่องปัดและบางครั้งกลับไม่สามารถเข้าถึงได้สำหรับที่อยู่ซึ่งเราสามารถ ping ได้

ฉันลองทำสิ่งต่อไปนี้:

ping -c 1 <fqdn>และตรวจสอบไฟล์exit status.

ใช้ได้กับทุกกรณีที่ฉันลองแล้วInetAddress.isReachableไม่ได้ผล

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.