# InetAdress->isreachable() unzuverlässig?



## tuxedo (23. Apr 2007)

Hallo,

hab mal eben mit Inetadress->isReachable experimentiert.

Wollte mir ne kleine Anwendung schreiben die meine Router, Server und ein paar andere Clients im Netz überwacht.

Doch dabei ist mir aufgefallen dass das anscheinend nicht zuverlässig funktioniert:


```
InetAddress adr;
			boolean hostAvailable = false;

			try {
				adr = InetAddress.getByName(host);
				hostAvailable = adr.isReachable(60000);	
			} catch (UnknownHostException e1) {
				// TODO Auto-generated catch block
				e1.printStackTrace();
			} catch (IOException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
```

Das Programm soll mehrere Einträge gleichzeitig überwachen können. Also läuft der Programmcode pro IP-Adresse die ich überwachen will in nem eigenen Thread in einer Endlosschleife mit 3 Sekunden Wartezeit zwischen zwei Überprüfungen.

Wie man sieht hab ich das TimeOut schon auf 60sek erhöht. Stelle ich das auf 2000ms oder sogar auf 1000ms dann sind seeeeeehr häufig manche Hosts auf einmal "nicht erreichbar". Beim nächsten Check sind die es dann doch wieder. Ein Windows-Ping nebenher zeigt aber keine Auffälligkeiten.

Und eine IP in meinem Netz (die Basisstation meines Siemens Gigaset VoIP-Telefons) ist laut Java gar nie "erreichbar". Windows-Pings und Webinterface der Basis gehen jedoch 1a. Auch die Antwortzeit ist ausreichend klein.

Weiß jemand woran das liegt?

- Warum werden manche Adressen nie als "reachable" erkannt?
- Warum kommts immer wieder zu Fehlerwerten die "offline" zeigen obwohl das Gerät dauerhaft online ist und ein Windows-Ping auch keinen aussetzer erkennt?!

Gruß Alex


----------



## moormaster (23. Apr 2007)

http://java.sun.com/j2se/1.5.0/docs/api/java/net/InetAddress.html#isReachable(int)

Dort ist zu lesen, dass wenn es das Betriebssystem (insbesondere die Rechte des Users) zulassen, ICMP ECHO REQUESTS (sprich pings) gesendet werden. ICMP ist jedoch kein Protokoll, welches die Übetragung der Pakete sicher stellt. Das heisst, bei der Übertragung verloren gegangene Pakete werden nicht automatisch nochmal gesendet.

So kann es sowohl bei ICMP immer dazu kommen, dass einzelne Anfragen unbeantwortet bleiben (weil entweder der Request oder die Antwort nicht angekommen ist).


----------



## tuxedo (23. Apr 2007)

Hmm, das weiß ich eigtl. Aber ich versteh nicht warum in 5 min kein einziger Ping von der Windows-Console aus fehlgeschlagen ist während in Java bei zu niedriger TimeOut Zeit das ganze richtig übel wird.

Und es erklärt auch nicht ganz warum meine Gigaset-Basisstation mit Java nicht überprüft werden kann... 

Ich hab mal die Zeit gemessen die isReachable braucht...

Die "Ausreißer" liegen meist im 10..15sek. Bereich.

Meine Gigasetbasis liegt immer um 20..21sek. Das Timeout steht jedoch auf 60sek ...

Ist schon etwas seltsam dass da so viel "verloren" geht wenn man das in Java macht.

- Alex


----------



## tuxedo (23. Apr 2007)

Oder gibts ne andere Möglichkeit die Erreichbarkeit einer IP zu ermitteln ohne dass man einen offenen Port an der zu ermittelnden IP haben muss?

- Alex


----------



## moormaster (23. Apr 2007)

Nix plattformunabhängiges 

Unter Windows XP kannst du zum Beispiel den Rückgabewert des ping Befehls abfragen.
dieser ist 1 falls ping timeout, 0 falls der host erreichbar ist; kann jedoch auch noch andere Werte für diverse Sondersituationen annehmen... muss man vllt. mal gucken, ob der irgendwo dokumentiert ist.

Variante 2 wäre das nutzen einer speziellen ICMP oder Raw Socket Library, mit der man quasi selbst die ping Requests absendet und auf Ergebnisse wartet.

Das ist soweit, was mir als Alternative für die API internen Sachen einfällt.


----------



## tuxedo (23. Apr 2007)

Problem gelöst:

Wenn ich versuche die Verbindung zu einer IP mit einem X-beliebigen Port zu öffnen dann gibts 3 Möglichkeiten:

1.) java.net.ConnectException: Connection timed out: connect -> Dauert bis zu einigen Sekunden: Der Rechner existiert wohlmöglich gar nicht oder die Verbindungsqualität ist unter aller sau so dass er "nicht wirklich zu gebrauchen ist"

2.) java.net.ConnectException: Connection refused: connect -> Geht ziemlich fix, <1sek: Der Rechner existiert, nimmt die Verbindung aber nicht an da er den Port nicht offen hat

3.) Kein Fehler -> IP Erreichbar, Port offen

Denke damit kann ich die Erreichbarkeit abfragen. Eine Zeitmessung wie bei Ping geht da nicht, stört mich aber nicht. Mir gehts nur drum ob die IP erreichbar ist.

Der Witz ist:

Warum ist SUN da nicht drauf gekommen?! Weil "isReachable" ist ja auch kein Ping, sondern nur ein Erreichbarkeitstest..

- Alex


----------



## tuxedo (23. Apr 2007)

Die Sache hat nur einen Haken:

Wenn der angefragte Client eine Firewall hat die Fragen auf unzulässige/nicht offene Ports nicht blockt ("reject") sondern ins leere laufen lässt ("drop") dann kommts auch zu "java.net.ConnectException: Connection timed out: connect" Meldungen.

Speziell auf meine Gigaset-Basis bezogen ist das der Fall. Somit krieg ich da auf Port 7 ein Timeout. 


Also müsste ich da auf nen definitiv offenen Port testen.

Ist also doch keine 100%ige Lösung. Aber 99%ig 

[update]

isReachable() macht scheinbar auch nix anderes... (http://forum.java.sun.com/thread.jspa?threadID=677862&start=15&tstart=0) Nur wird hier Port 7 (Echo-Dienst) verwendet ... Für Rechner/Geräte die per Drop-Regel in der Firewall gegen sowas "geschützt" sind taugt der Test dann halt nix.


----------



## Wildcard (23. Apr 2007)

alex0801 hat gesagt.:
			
		

> Wenn der angefragte Client eine Firewall hat die Fragen auf unzulässige/nicht offene Ports nicht blockt ("reject") sondern ins leere laufen lässt ("drop") dann kommts auch zu "java.net.ConnectException: Connection timed out: connect" Meldungen.
> 
> Speziell auf meine Gigaset-Basis bezogen ist das der Fall. Somit krieg ich da auf Port 7 ein Timeout.


http://www.chiark.greenend.org.uk/~peterb/network/drop-vs-reject


----------



## tuxedo (23. Apr 2007)

Wildcard hat gesagt.:
			
		

> http://www.chiark.greenend.org.uk/~peterb/network/drop-vs-reject



Hmm ...



> DROP (aka DENY, BLACKHOLE)
> Prohibit a packet from passing. Send no response.



Das ist genau das was die Gigaset-Basis macht... Es kommt zu einem Timeout bei der Anfrage an Port7 den isReachable testet. Meine anderen Router machen da dann schon ein REJECT:



> REJECT
> Prohibit a packet from passing. Send an ICMP destination-unreachable back to the source host [unless the icmp would not normally be permitted, eg. if it is to/from the broadcast address].



So weiß dann Java dass der Verbindungsaufbau von der Gegenstelle abgewiesen wurde. Und daras lässt sich Schlussfolgern dass die Gegenstelle da sein muss... 

Aber ich nehme an das hat zur Ergänzung und nicht zur Korrektur gedient?!

Hab mir ne kleine Hilfsklasse gebastelt:


```
import java.io.IOException;
import java.net.ConnectException;
import java.net.Socket;
import java.net.UnknownHostException;

/**
 * Einfache Hilfsklasse zum ermitteln ob ein Host online ist
 * @author achristian
 *
 */
public class CheckIp {
	
	public boolean DEBUG = false;
	public int CHECKPORT = 80;

	/**
	 * Prüft ob ein Host erreichbar ist. 
	 * Achtung: Wenn der Host eine Firewall-Einstellung 
	 * hat die unzulässige-Portanfragen "dropped" dann 
	 * wird der Online-Status des Host nicht erkannt.
	 * 
	 * Ist der Port am Host offen erhält man einen 
	 * repräsentativen Zeitwert in Millisekunden für den 
	 * Verbindungsaufbau (ähnlich der Ping-Zeit).
	 * 
	 * Ist der Port nicht offen liegt die ermittelte Zeit 
	 * meist um 1000ms.
	 * 
	 * Wird die Portanfrage gedropped oder ist der Host
	 * nicht online erhält man -1
	 *  
	 * @param host der getestet werden soll
	 * @return Zeit in Millisekunden
	 */
	public int isReachable(String host){
		
		long start = System.currentTimeMillis();
	    
		try {
		
			Socket socket = new Socket(host, CHECKPORT);
			socket.close();
			
	    } catch (ConnectException e){
	    	
	    	String ex = e.toString();
	    	
	    	if (ex.contains("Connection refused")){
	    		
	    		long end = System.currentTimeMillis()-start;
	    		if (DEBUG)System.out.println("online, indirekt ermittelt");
	    		return (int)end;
	    		
	    	} else {
	    		
	    		if (DEBUG) System.out.println("offline");
	    		return -1;
	    		
	    	}
	    	
	    } catch (UnknownHostException e) {
			
	    	if (DEBUG) System.out.println("offline");
    		return -1;
			
		} catch (IOException e) {
			
			if (DEBUG) System.out.println("offline");
    		return -1;
    		
		}
		
		long end = System.currentTimeMillis()-start;
		if (DEBUG)System.out.println("online");
		return (int)end;
	}
	
	public static void main(String[] args) {
		System.out.println(new CheckIp().isReachable("127.0.0.1"));
		System.out.println(new CheckIp().isReachable("www.google.de"));
		System.out.println(new CheckIp().isReachable("www.dieadressegibtsgarnicht.de"));
	}
	
}
```

vielleicht kann's ja jemand gebrauchen... Die SUN-Version lässt ja keine Änderung des Ports zu und testet IMMER auf Port 7 ... Zumindest hab ich nix gefunden womit man das umstellen kann.

Gruß
Alex

P.S. Oder gings hier drum??



> Conclusion
> 
> DROP offers no effective barrier to hostile forces but can dramatically slow down applications run by legitimate users. DROP should not normally be used.



Naja, Java hat da ja ein Timeout drin.. Nach rund 20 Sek bricht das ganze ab.. Für mich ist das nicht tragisch. Läuft eh in nem eigenen Thread.


----------

