| Community |
|
|
|
2 registrierte Benutzer online: Alina, Sigrid.Z
Der Rekord waren 20 angemeldete Benutzer am So 15. Nov 2009, 17.07 Uhr.
Farben: Moderator, Administrator
|
|
|
|
|
| Autor |
Nachricht |
|
Bernhard Frauendienst
Admin


Beiträge: 5786
Karma: +239
|
Verfasst am: Mi 05.11.08, 21:36
Titel: RMI übers Internet
|
|
|
Nachdem von einigen Gruppen die Frage kam, wie sie RMI übers Internet testen können, hier eine kleine Erklärung und ein paar Lösungswege:
ProblematikIm Grunde ist das Internet technisch gesehen auf IP-Ebene auch nicht anders zu betrachten als ein lokales Netzwerk. Und RMI läuft ausschließlich über TCP, also noch “über” IP.
Wo liegt dann das Problem, im lokalen Netz läuft ja alles. Nun, das Problem ist, dass im Zeitalter des DSL-Booms und der IPv4-Adressen-Knappheit nahezu jeder einen Router zu Hause hat, aber nur eine öffentliche IP-Adresse. Deswegen verwenden diese Router eine Technik namens NAT (Network Address Translation), um mehreren Rechner, die nur private IP-Adressen zugewiesen haben, einen transparenten Zugang zum Internet zu erlauben.
Das Problem ist nun, dass dieser “transparente Zugang” nur in eine Richtung funktioniert, nämlich von innen nach außen. Eingehende Verbindungen hingegen kann der Router nicht ohne weiteres zuordnen und an den richtigen Rechner weiterschicken, das erfordert zusätzlich Konfiguration (“Ports freischalten” oder “Ports weiterleiten” sind hier gängige Begriffe).
Welche Ports werden benötigt?Nun erfordert RMI aber einige offene Ports, auf denen es eingehende Verbindungen empfangen kann. Dies ist zum einen der Port für die RMI Registry (standardmäßig 1099). Weiterhin erfordert jede Programm-Instanz einen weiteren Port, auf dem es Objekte exportieren kann.
LösungsmöglichkeitenIm Folgenden sollen einige Lösungsmöglichkeiten dargestellt werden, die jeweils ihre Vor- und Nachteile haben. Die Auflistung ist nach meiner persönlichen Empfehlung abgestuft, die unter anderem auf Eleganz und “Praktikabilität” der jeweiligen Lösung basiert.
IPv6-TunnelWie bereits erwähnt rührt diese Problem hauptsächlich von der Knappheit an IPv4-Adressen her (inzwischen wird zwar NAT auch gerne als Sicherheitstechnik angeführt, ich persönlich glaube jedoch nicht, dass es ohne die Adressen-Knappheit soweit gekommen wäre). Da auch NAT vielmehr Symptombekämpfung als Lösung ist, gibt es inzwischen einen Nachfolger für IPv4, nämlich IPv6. Der für uns entscheidende Unterschied ist, dass die Adressen 128bit haben (statt 32bit bei IPv4), und deswegen für jedermann ausreichend (öffentliche) Adressen vorhanden sind.
Alle aktuellen Betriebssysteme bringen von Haus aus Unterstützung für IPv6 mit.
Nur die Verbreitung läuft aus verschiedenen Gründen noch etwas schleichend, kaum ein ISP bietet native IPv6-Konnektivität an. Deswegen gibt es verschiedene Möglichkeiten, auch mit einer IPv4-Verbindung IPv6 nutzen zu können. Die für uns interessante Methode sind dabei sogenannte IPv6-Tunnel, bei denen man eine feste IPv6-Adresse (und bei Bedarf auch ein ganzes Subnetz, wesentlich größer als der gesamte IPv4-Adressraum ) zugewiesen bekommt.
Solche Tunnel kann man beispielsweise bei dem Tunnel-Broker SiXXs kostenlos beziehen. Diesen kann man dann entweder von einer statischen IPv4-Adresse, oder mit einem kleinen Software-Programm auch problemlos von einer “dynamischen” Adresse benutzen.
Eine Anleitung für die Einrichtung eines solchen Tunnels würde den Rahmen dieses (ohnehin schon langen) Beitrags sprengen, deswegen sei hier nur auf einschlägige Seiten im Netz verwiesen, bei denen man ganz gut aufgehoben ist. Nur noch zwei kleine Anmerkungen:- M-Net bietet für seine Kunden einen eigenen PoP (Point of Presence) an, der direkt ans m6bone angeschlossen ist. Somit kann man als M-Net-Kunde also IPv6 mit einer Performance nutzen, die nahe an native IPv6-Konnektivität herankommt.
- Bei der Anmeldung bei SiXXs muss man seine echten Daten angeben. Die Eingaben werden von realen Personen gelesen (einer davon ist übrigens bei uns im IRC-Channel erreichbar), also garnicht erst mit irgendwelchen Fake-Daten probieren

Am besten einfach mal die SiXXs-Einrichtungs-FAQ lesen, und sich mit der Campus-Adresse anmelden.
[ UPDATE: Berni hat mir grade mitgeteilt, dass Leute z.B. in Wohnheimen auch direkt über das LRZ IPv6-Konnektivität erlangen können. Informationen hierzu gibts auf der LRZ ISATAP Website. ]
Vor- und Nachteile der Methode:
Man kommt nach einer kurzen Einrichtung in den Genuss eines gesamten IPv6-Subnets und kann sich somit schonmal auf die Umstellung auf IPv6 vorbereiten. Da es sich hiermit um eine zukunftsweisende Technologie handelt, ist dies sicher keine vertane Zeit.
Allerdings müssen natürlich beide Seiten der Verbindung IPv6 beherrschen, am einfachsten wäre es also, wenn gleich die ganze Gruppe auf IPv6 umsteigt ;)
Möglicherweise nicht die “einfachste” Methode, aber im Sinne des IPv6-spirit meine definitive Empfehlung.
Virtual Private NetworkEine weitere Möglichkeit, die Heim-NAT-Problematik zu umgehen, ist über Virtual Private Networks. Hier bietet sich beispielsweise das VPN vom LRZ an, da hier alle Teilnehmer des Praktikums Zugriff haben (über ihre Campus oder CIP-Pool-Kennung).
Weitere Informationen zum LRZ-VPN finden sich auf der LRZ VPN-Website, und auch hier im Forum finden sich einige Beiträge zu diesem Thema.
Zu beachten gilt, dass natürlich die vom VPN zugewiesene VPN-interne IP-Adresse verwendet werden muss, um zur Registry etc. zu verbinden.
Alternativ kann man auch Hamachi verwenden, um innerhalb der Gruppe ein eigenes kleines VPN zu erstellen.
Vor- und Nachteile der Methode:
Viele Leute haben bereits den VPN-Zugang zum LRZ auf ihrem Rechner eingerichtet. Auch hier gilt, dass aber dann die ganze Gruppe zum VPN verbunden sein muss.
Ports im Router weiterleitenDie meisten Router bieten die Möglichkeit, einzelne Ports statisch auf eine bestimmte (interne) Adresse weiterzuleiten. Wie bereits erwähnt läuft dies gängigerweise unter Namen wie “Ports freischalten”, “Ports weiterleiten”, “Dynamic Server”, oder ähnlichen herstellereigenen Kuriositäten :)
Im Grunde sagt man damit nur dem Router, zu welcher internen IP er eingehende Verbindungen auf bestimmten Ports weiterleiten (umschreiben und routen) soll.
Diese Methode ist wohl konfigurations-technisch die einfachste, hat aber einen entscheidenden Nachteil: da nur bestimmte Ports weitergeleitet werden, muss man wissen, auf welchem Port denn überhaupt Verbindungen eingehen werden.
Bei der RMI registry ist dies noch recht einfach, diese läuft standardmäßig auf 1099.
Beim Exportieren von Objekten sieht dies anders aus. Der Port hierfür wird beim Aufruf von UnicastRemoteObject.exportObject(...) als zweiter Parameter angegeben. Verwendet man hier 0 (Null = zufälliger Port), so sucht sich Java pro JVM einen unbenutzten Port. Das ist praktisch in einer Umgebung, in der alle Ports frei zugänglich sind, in restriktiveren Umgebungen (hinter Firewalls, NATs, etc.) kann das aber unhandlich werden, da zumindest mir keine Firewall bekannt ist, die ohne Konfiguration eine transparente RMI-Benutzung erlaubt (ob es technisch möglich ist, würde noch etwas mehr Recherche erfordern, auf den ersten Blick würde ich es aber bejahen ).
Deswegen empfiehlt es sich, das Programm so zu gestalten, dass man den verwendeten Port manuell auswählen kann. Dies lässt sich beispielsweise so gestalten:
Erbt man von UnicastObject, anstatt den Stub “per Hand” zu exportieren, so wird exportObject(..) im Konstruktor der Elternklasse aufgerufen.
Verwendet man hier den Aufruf super();, so entspricht dies dem Aufruf von UnicastObject.exportObject(..., 0);, es wird also wieder ein zufälliger Port ausgewählt. Hier sollte man also besser einen Parameter int port in den Konstruktor aufnehmen, der dann an den Super-Konstruktor übergeben wird:
Da das Erben von UnicastRemoteObject ansonsten aber ohnehin keine Vorteile bringt, würde ich ganz darauf verzichten, und den Stub immer über exportObject(...) exportieren.
Da die RMI Registry und der Server wohl häufig auf dem gleichen Rechner laufen werden, kann man sich einen Port “sparen”, indem man die Registry direkt im Programm-Code erstellt, und nicht gesondert startet. Dies hat natürlich den “Nachteil”, dass Server und Registry auf der gleichen Maschine laufen müssen. Deswegen empfiehlt es sich, die Möglichkeit, zu einer entfernten Registry zu verbinden, offenzuhalten (etwa über einen command-line parameter).
Um eine eigene Registry für den Server-Prozess zu erstellen, verwendet man einfach LocateRegistry.createRegistry(...) anstatt LocateRegistry.getRegistry(...). Hier kann man dann natürlich keinen host mehr übergeben, sondern nur noch einen Port. Dieser ist dann allerdings nicht mehr optional, um den Standard-Port 1099 zu verwenden, kann man auf die Konstante Registry.REGISTRY_PORT zurückgreifen.
Das Ganze könnte dann analog zum Beispiel oben etwa so aussehen:
...
public static void main(String[] args) {
String host = null;
if (args.length > 0) {
host = args[0];
}
...
Registry registry;
if (host != null && host.trim().length() > 0) {
registry = LocateRegistry.getRegistry(host);
} else {
registry = LocateRegistry.createRegistry(Registry.REGISTRY_PORT);
}
...
}
Natürlich sollte man den Port nicht festschreiben, wie hier im Beispiel, sondern ebenfalls als Einstellung akzeptieren (wiederum wie erwähnt z.B. per Kommandozeile, Config file, oder GUI).
Eventuell muss bei dieser Methode noch die externe Adresse des Rechners mit angegeben werden, damit diese bei der Erstellung der Stubs richtig angegeben wird. Dies kann man über die JVM-Variable java.rmi.server.hostname machen, also z.B.:
java -Djava.rmi.server.hostname=<externeIP>
(Dazu muss ich sagen, dass meine Tests (java 1.5 auf beiden Seiten) ohne diesen Parameter funktioniert haben. Allerdings habe ich auch keine codebase angegeben, und trotzdem konnte ich chatten. Wohl ein Fall von Yphrum’s Law )
Vor- und Nachteile der Methode:
Funktioniert mit den meisten Setups, bei denen man Ports im Router weiterleiten kann (geht z.B. nicht, wenn man im Wohnheim hinter dem NAT-o-MAT sitzt). Außerdem muss im Programm-Code festgelegt werden, welche Ports verwendet werden. Will man hier noch zusätzlich aus einer bestimmten Range einen freien Port auswählen, kann das ziemlich ausufern 
Eigene SocketFactoriesEine letzte Methode sei noch der Vollständigheit halber erwähnt. Mir persönlich gefällt sie allerdings weniger, da es mehr Symptombekämpfung ist, als das Problem an der Wurzel zu packen. Sie ist allerdings einsetzbar, wenn keine der oberen Methoden möglich sind (weil man etwa hinter einem NAT sitzt, bei dem man keine Ports weiterleiten kann/darf, und zusätzlich noch die oben verwendeten Tunnel-Protokolle geblockt werden. So ein Setup gibt es aber in der Regel höchstens in restriktiven Firmen-Umgebungen, im privaten Bereich ist mir so etwas noch nicht untergekommen. Im Wohnheim z.B. war bei mir sowohl IPv6 möglich, außerdem konnte man direkt auf die im LRZ gerouteten privaten Adresse-Bereiche zugreifen, also z.B. auf die VPN-internen Adressen der anderen Gruppen-Mitglieder bei Verwendung der LRZ-VPN-Variante).
Eventuell ist euch beim Durchlesen der Java APIDocs aufgefallen, dass sowohl von LocateRegistry.getRegistry(...) als auch von UnicastRemoteObject.exportObject(...) Varianten existieren, die eine oder mehrere RMISocketFactory(s) entgegennehmen.
Dies ermöglicht dem Programmier die Art und Weise zu verändern, auf die die von RMI verwendeten Sockets miteinenader kommunizieren. Dazu gibt es ein paar Implementierungen (s.u.), die die Sockets bzw. deren Protokoll dahingehend modifizieren, dass sie auch bei Callbacks nur neue Verbindungen in eine Richtung aufbauen.
So werden nur noch beim Server eingehende Verbindungen benötigt (hier müssen aber natürlich wieder entweder Ports forwarded werden, oder der Server muss anderweitig schon übers Internet erreichbar sein).
Ich gebe hier keine genaue Gebrauchsanleitung, weil ich mir die Implementierungen erstens selbst nicht so genau angeschaut habe, und zweitens sich jeder, der sie verwendet, dessen bewusst sein soll was er tut 
Ich habe mehrere Implementierungen gefunden:
Desweiteren noch ein paar kommerzielle Lösungen, die wohl hier nicht in Frage kommen.
Vor- und Nachteile der Methode:
Funktioniert auch in recht restriktiven Umgebungen, allerdings wird der Code dahin modifiziert, dass er nicht mehr mit z.B. dem Endspiel-Server kompatibel ist (dieser müsste dann auch die verwendete SocketFactory verwenden). Deswegen ist es unausweichlich, die Verwendungen dieser SocketFactories konfigurierbar zu machen.
Außerdem verliert die Übertragung aufgrund der Tunnelung etwas an Performance (über HTTP-Connect z.B. etwa 1:3), was bei größeren Datenmengen schon ins Gewicht fallen kann.
Insgesamt würde ich diese Methode aber ohnehin nicht empfehlen. Interessant ist sie dennoch 
So, das war jetzt glaube ich mein längster Post hier im Forum Welche der Methoden ihr verwendet, könnt ihr natürlich selbst entscheiden. Sowohl meine Empfehlung, als auch die jeweiligen Vor- und Nachteile habe ich aufgeschrieben, eine Entscheidung sollte also durchaus fällbar sein.
Wenn es noch Fragen, Hinweise, oder sogar Korrekturen, Verbesserungsvorschläge, oder Methoden die ich übersehen habe, gibt, dann sind diese natürlich herzlich willkommen!
Zum Abschluss noch ein kleiner Tip: wenn man mehr als nur ein/zwei Parameter über die Kommando-Zeile übergeben will (wie man es von vielen Linux-Tools kennt), dann empfiehlt sich der Einsatz eines Kommando-Zeilen-Parsers.
Ich habe dafür immer apache-commons/cli verwendet, der mir auch recht gute Dienste geleistet hat.
Bei einer kurzen Suche habe ich grade noch einen Parser auf google-code gefunden, der Annotations verwendet, und somit schonmal einen sehr leet’en Eindruck macht Getestet habe ich ihn allerdings nicht, würde mich aber über Erfahrungen und Eindrücke freuen.
Beste Grüße
Bernhard
[ UPDATE 2009-11-24: Neue Forums-Features eingebaut ]
|
|
|
|
|
|
|
0
|
|
|
|
|
|
RobertK
Implementor


Beiträge: 316
Karma: +117
|
Verfasst am: Do 13.11.08, 16:42
Titel:
|
|
|
Also hab den ganzen Client-Server Käse zum laufen gebracht lokal. Jetz haben wir das heute übers LRZ-VPN probiert und dann passiert jenes:
| Zitat: |
Verbinde mit Server: 10.158.59.125:1070
java.rmi.ConnectIOException: Exception creating connection to: 10.158.59.125; nested exception is:
java.net.NoRouteToHostException: No route to host |
erm...ja ?!
Übrigens: Es ist absolut gewollt, dass er nicht den Standardport nimmt etc. Das alles funktioniert ja lokal auch...
|
|
|
|
|
|
|
0
|
|
|
|
|
|
Bernhard Frauendienst
Admin


Beiträge: 5786
Karma: +239
|
Verfasst am: Do 13.11.08, 19:01
Titel:
|
|
| RobertK hat Folgendes geschrieben: |
Also hab den ganzen Client-Server Käse zum laufen gebracht lokal. Jetz haben wir das heute übers LRZ-VPN probiert und dann passiert jenes:
| Zitat: |
Verbinde mit Server: 10.158.59.125:1070
java.rmi.ConnectIOException: Exception creating connection to: 10.158.59.125; nested exception is:
java.net.NoRouteToHostException: No route to host |
erm...ja ?!
Übrigens: Es ist absolut gewollt, dass er nicht den Standardport nimmt etc. Das alles funktioniert ja lokal auch... |
Ja, also wenn du keine Route zu dem Host hast, dann kann Java da auch nix dafür Und mit Ports hat das dann sowieso nichts zu tun (außer bei ner ganz komischen Firewall-Konfiguration, aber sowas macht ja niemand ).
Also wenn ich von zu Hause zum VPN connecte, kann ich andere clients pingen, die auch zum VPN connected sind.
Deine Adresse sieht nach WLAN aus, möglicherweise geht es damit nicht (das kann ich von hier schlecht testen, und auch sonst nur schlecht, da mein Laptop grade etwas ... kaputt ist). WLAN-Clients kriegen nur interne Adressen, die dann (nach "außen") über den NAT-o-MAT geroutet werden. Eingehende Verbindungen können die als allenfalls aus dem MWN empfangen, sofern die Firewall mitspielt.
Von welcher Adresse aus wolltest du denn zu 10.158.59.125 verbinden? Und wie sah das Setup aus (welcher ISP (LRZ, DialUp, ...)?, Netztopologie (Router, NAT, ...)?, VPN (welche VPN-Adresse, ...)?).
Ansonsten kann ich mir noch vorstellen, dass der VPN Client keine Routen pushed (hast du "Allow Local Lan" aktiviert oder so?). Das halte ich aber eher für unwahrscheinlich.
|
|
|
|
|
|
|
0
|
|
|
|
|
|
Bernhard Frauendienst
Admin


Beiträge: 5786
Karma: +239
|
Verfasst am: Do 13.11.08, 21:01
Titel:
|
|
|
So, ok, ich hab jetzt nochmal nachgeforscht (danke Berni), und das Problem ist Folgendes:
10.158.59,x ist die vom WLAN-AP zugewiesene Adresse, nicht die vom VPN. Wie ich in meinem Beitrag beschrieben habe, muss man die VPN-Adresse verwenden, zwischen den LRZ-WLAN-Clients ist (zumindest am gleichen AP) keine Kommunikation möglich.
Konkret bedeutet das also, dass man die Adresse des "VPN-Adapters" verwenden muss (unter Linux etwa tun0, unter Windows ebenfalls als Netzwerkinterface eingetragen, an dessen Name ich mich aber nicht mehr erinnere).
|
|
|
|
|
|
|
0
|
|
|
|
|
|
RobertK
Implementor


Beiträge: 316
Karma: +117
|
Verfasst am: Do 13.11.08, 22:52
Titel:
|
|
| Zitat: |
Ja, also wenn du keine Route zu dem Host hast, dann kann Java da auch nix dafür Und mit Ports hat das dann sowieso nichts zu tun (außer bei ner ganz komischen Firewall-Konfiguration, aber sowas macht ja niemand ). |
Ja versteh mich nicht falsch, habs ja nicht auf Java geschoben. Wollt auch ned sagen, dass ich es auf die Ports schieb, aber mit der Zeit wirds frustrierend, weil man auch keine ordentlichen Anleitungen im Internet findet, da es jeder irgendwie anders macht und kaum einer erklärt was nun dies und jenes bedeutet, geschweige denn was denn sei KÖNNTE wenn gewisse Exceptions auftreten....
Ich probiers jetzt einfach bei mir im Netzwerk und wenns dann dort geht dann werd ichs doch noch übers Internet hinkriegen...
|
|
|
|
|
|
|
0
|
|
|
|
|
|
Bastian Gebhardt
Admin


Beiträge: 993
Karma: +96
|
Verfasst am: Fr 14.11.08, 18:55
Titel:
|
|
| Bernhard Frauendienst hat Folgendes geschrieben: |
So, ok, ich hab jetzt nochmal nachgeforscht (danke Berni), und das Problem ist Folgendes:
10.158.59,x ist die vom WLAN-AP zugewiesene Adresse, nicht die vom VPN. Wie ich in meinem Beitrag beschrieben habe, muss man die VPN-Adresse verwenden, zwischen den LRZ-WLAN-Clients ist (zumindest am gleichen AP) keine Kommunikation möglich. |
Genau, die 10.x.x.x ist die ohne VPN, die 141.x.x.x ist die VPN IP. Leider heißt diese Fehlermeldung nicht unbedingt dass man die 10er verwendet hat...
Eine Gruppe von mir hat den Chat von Laptop zu Laptop zeigen wollen, und es ging nicht weil bei einem von beiden die Softwarefirewall zugemacht hat.
Als Fehlermeldung hat er aber auch gesagt dass es sich nicht mit der 10er verbinden kann...
|
|
|
_________________ When I see a bird that walks and swims like a duck, I call that bird a duck. And if Chuck Norris says it's a cow, THEN IT'S A COW GODDAMMIT!
alles über den IRC-Channel (mit Webclient)
|
|
|
0
|
|
|
|
|
|
RobertK
Implementor


Beiträge: 316
Karma: +117
|
Verfasst am: Sa 15.11.08, 3:59
Titel:
|
|
|
Also hab hier ein kleines Problem und wär froh um einen Tipp...
Ich kann mit meinem Client-Server Code sowohl lokal als auch im Netzwerk connecten wie ich lustig bin (auch Methodenaufrufe starten).
Jetzt habe ich folgendes Problem: Ich habe zwei Netzwerke. In dem einen läuft mein Server und in dem anderen mein Client. Ich übergebe jetzt meinem Client die Adresse des Routers des Netzwerks in dem mein Server sitzt. Allerdings meint mein Client dann er hat kein "route to host". Dabei gibt er mir aber die lokale Netzwerkadresse des Servers an!
Portforwarding,Firewall etc wurde alles eingestellt, daran liegts also nicht.
Das wirklich komische ist, dass er das nur macht wenn ich einen Methodenaufruf dabei habe. Ich hab dann mal ausprobiert, dass er nach der Zeile "Naming.lookup" (auf Client Seite) einfach mal ausgeben soll "Verbunden". Das alles macht er ohne murren und knurren.
Irgendjemand einen Tipp was da los ist ?
|
|
|
|
|
|
|
0
|
|
|
|
|
|
Bernhard Frauendienst
Admin


Beiträge: 5786
Karma: +239
|
Verfasst am: Sa 15.11.08, 20:30
Titel:
|
|
|
Versuch mal den Server mit java.rmi.server.hostname zu starten (siehe oben). Dabei als hostname die IP angeben, über die der Server vom Client aus erreichbar ist.
|
|
|
|
|
|
|
0
|
|
|
|
|
|
RobertK
Implementor


Beiträge: 316
Karma: +117
|
|
|
|
|
|
|
0
|
|
|
|
|
|
RobertK
Implementor


Beiträge: 316
Karma: +117
|
|
|
|
|
|
|
0
|
|
|
|
|
|
Bernhard Frauendienst
Admin


Beiträge: 5786
Karma: +239
|
|
|
|
|
|
|
0
|
|
|
|
|
|
RobertK
Implementor


Beiträge: 316
Karma: +117
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|