Sockets - wie legt man die lokale IP fest?

Hallo,

öffnet man einen Socket nur mit einer Ziel-IP wird eine lokale IP-Adresse verwendet, die in der Klasse als SO_BINDADDR definiert ist. Nun frage ich mich, wo diese gesetzt wird und ob man daruaf Einfluss nehmen kann.
Es geht mir darum das OHNE den Konstruktor der Klasse Socket zu machen, der eine Angabe der loakalen IP ermöglicht. Hintergrund ist, das alle Java Programme die ich starte eine bestimmte lokale IP verwenden sollen. Auf die Implementierung der Programme habe ich keinen Einfluss.

Danke und Grüße

Bonkers

hi Bonkers

verstehe das nicht ganz. die lokale ip wird doch vom betriebssystem festgelegt. da kann man im programm nicht wirklich was auswählen. wäre ja auch sinnlos - wie soll mich denn das datenpaket finden, wenn ich im java-programm eine andere ip mitgebe als ich tatsächlich habe.

grundsätzlich ist es so: ich kann pro netzadapter eine ip-adresse festlegen. diese ip-adresse sollte im lokalen netz eindeutig sein, da es sonst zu ziemlichen problemen kommen kann. welcher adapter und damit welche ip-adresse verwendet wird, wenn ich zu einer anderen ip-adresse verbinde ist abhängig vom routing auf meinem rechner. sind spezielle routen für bestimmte ziel-adressen definiert, so werden die in diesen routen definierte netzadapter verwendet. ansonsten wird ein defaultadapter verwendet.

dementsprechend kann man den konstruktor für einen socket auch keine ip-adresse mitgeben, da die das betriebssystem zum zeitpunkt des aufbaus der verbindung aus den routing-tabellen ermittelt. normalerweise ist es ja auch für das client-programm völlig wurscht, welche ip verwendet wird. und wenn nicht, dann muss man das halt in den routing-tabellen eintragen.

lg
erwin

Hi Bonkers,

öffnet man einen Socket nur mit einer Ziel-IP wird eine lokale
IP-Adresse verwendet, die in der Klasse als SO_BINDADDR
definiert ist. Nun frage ich mich, wo diese gesetzt wird und
ob man daruaf Einfluss nehmen kann.

du meinst vermutlich, weil sonst auf 0.0.0.0 gebunden wird - sprich auf alle IP# des betreffenden Hosts? Und das ist oft nicht gewünscht, z.B. im Shared Hosting aus Abrechnungsgründen.

Mir fällt da nur ein, die entsprechenden Betriebsystem-Routinen abzufangen, wenn man nicht in die Software selbst eingreifen kann - bzw. diese sowas nicht von sich aus vorsieht. Üblicherweise kann man jedem Programm eine eigene System-Library unterschieben, jedenfalls geht das unter UNIXoiden Systemen. Dort könnte man 0.0.0.0 auf was immer man will „umbiegen“ und dann die oringinal-System-Routine aufrufen.

Alles Gute wünscht
… Michael

Hi Erwin,

verstehe das nicht ganz. die lokale ip wird doch vom
betriebssystem festgelegt. da kann man im programm nicht
wirklich was auswählen. wäre ja auch sinnlos - wie soll mich
denn das datenpaket finden, wenn ich im java-programm eine
andere ip mitgebe als ich tatsächlich habe.

natürlich keine andere als man hat. Aber auf Multi-Homed Systemen macht das schon Sinn. Z.B. weil man auf einem Shared Server nur auf seine eigene IP# binden darf - oder sogar (per iptables) KANN. D.h. alle Programme, die 0.0.0.0 (alle IP#n des Hosts) versuchen, bekommen gar keinen Zugriff, weil eben nur eine IP# freigeschaltet ist.

Alles Gute wünscht
… Michael

hi Bonkers

verstehe das nicht ganz. die lokale ip wird doch vom
betriebssystem festgelegt. da kann man im programm nicht
wirklich was auswählen. wäre ja auch sinnlos - wie soll mich
denn das datenpaket finden, wenn ich im java-programm eine
andere ip mitgebe als ich tatsächlich habe.

Wenn ich auf einem System mehrere IP Adressen habe und die Kommunikation über eine bestimmt IP abwickeln will, dann ist das nicht sinnlos.

Die Situtaion ist folgende: Ich habe eine primäre IP Adresse und mehrere virtuelle IP Adressen(oder auch Alias IPs genannt, glaub ich). Mein Programm kommuniziert über die primäre IP, was mist ist, da diese auf der Firewall nicht freigegeben ist. Ich muss das Programm dazu bringen auf eine bestimmt Alias IP zu hören. Und mit Umstellen der Routing Tabelle ist das nicht möglich, der Admin haut mich sonst…

Hoffe jetzt ist es klarer.

dementsprechend kann man den konstruktor für einen socket auch
keine ip-adresse mitgeben, da die das betriebssystem zum
zeitpunkt des aufbaus der verbindung aus den routing-tabellen
ermittelt.

doch das kann man:

Socket(InetAddress address, int port, InetAddress localAddr, int localPort)

Aber wie gesagt das hilft mir nicht, das Programm verwendet nur:

Socket(InetAddress address, int port)

wobei der 1. Parameter die IP der Zieladresse ist und als lokale IP automatisch die primäre IP verwendet wird.

Grüße

B.

Moien

Es geht mir darum das OHNE den Konstruktor der Klasse Socket
zu machen, der eine Angabe der loakalen IP ermöglicht.

Neben dem Konstruktor gehts übrigens auch per:
Socket.bind (new InetSocketAddress(„deine IP“, port);

Hintergrund ist, das alle Java Programme die ich starte eine
bestimmte lokale IP verwenden sollen. Auf die Implementierung
der Programme habe ich keinen Einfluss.

Autsch. Dazu gibt es eine leicht krypische Notiz: http://java.sun.com/j2se/1.4.2/docs/api/java/net/Soc…

Wenn man eine selbstgebauten SocketFactory einbringt geht das. Allerdings braucht man dafür Zugriff auf den Quellcode. Von aussen geht das so nicht.

cu

hi bonkers

in der tat - meine erste meldung habe ich „aus dem bauch“ heraus geschrieben und nicht in der api nachgeschaut. daher nicht ganz richtig.

folgende möglichkeiten hast du:

  • auf einem windows-system kannst du über den ipconfig-befehl einzelne netzadapter deaktivieren bzw. wieder aktivieren. wenn dein java-programm gleich nach dem start die netzverbindung aufbaut, reicht es ev. in einem kleinen startskript zuerst alle unerwünschten ip-adressen zu deaktivieren, dann das programm zu starten und dann die ip-adressen wieder zu aktivieren. ist zwar nicht schön, sollte aber klappen.

  • unabhängig davon, ob man den source-code hat oder nicht: sofern der programmierer es nicht explizit verhindert hat, kann man das programm auch aus einem eigenen java-programm heraus aufrufen. die vm macht beim start ja auch nix anders als die main-klasse zu laden und die statische main-methode mit allen parametern aufzurufen. du kannst also einen kleinen java-wrapper schreiben, der die main-methode der eigentlichen klasse aufruft und dabei die parameter durchschleust. zuvor kann diese klasse noch ein Socket.setSocketImplFactory(factory) aufrufen und dabei eine eigene SocketFactory im System registrieren. Dieser Befehl kann in der vm nur einmal aufgerufen werden, d.h. das programm kann deine einstellung nicht mehr rückgängig machen. die factory kann man relativ leicht von der default-implementierung ableiten. die meisten methoden übernimmt man einfach so, allerdings gibt man für die lokale ip-adresse immer einen bestimmten wert vor.

zur zweiten möglichkeit ist halt zu sagen, dass es natürlich etliche fälle gibt, in denen das programm dann möglicherweise nicht mehr korrekt funktioniert. dies ist vor allem wenn:

  • das programm eigene classloader verwendet und nicht damit rechnet, nicht direkt von der vm gestartet zu werden
  • das programm eigentlich eine eigene socketfactory registrieren will
  • das programm zwingend einen security-manager voraussetzt und sich der mit deiner factory beisst

ansonsten sollte es eigentlich keine probleme geben, da deine factory ja einen normalen socket zurückliefert - nur eben mit einem anderen konstruktor. ich vermute also mal, dass es bei dir klappen müsste.

lg
erwin

1 Like

Moin Moin,

Wenn man eine selbstgebauten SocketFactory einbringt geht das.
Allerdings braucht man dafür Zugriff auf den Quellcode. Von
aussen geht das so nicht.

du bringst mich auf eine Idee: Evtl. geht’s über den Classloader - und dann eine andere Klasse unterschieben, die die IP# setzt und dann die normale Socket-Klasse instanziert.

Alles Gute wünscht
… Michael

Moien

du bringst mich auf eine Idee: Evtl. geht’s über den
Classloader -

ich erhöhe um einen Wrapper und die Methode
http://java.sun.com/j2se/1.4.2/docs/api/java/net/Soc…

Dann braucht man keinen Classloader mehr umbauen. Einfach nur vor dem starten der eigentlichen main-Methode die SocketFactory austauschen.

cu

1 Like

danke!
vielen Dank, jetzt verstehe ich das besser!

Nachtrag!!
Im Endeffekt geht es um ein Problem mit JBoss und dem WebService:

Wenn man sich den Verlauf des Ganzen anschaut, landet man irgendwann bei dem sun.net.NetworkClient des JDK, diese Klasse wird verwendet um die Verbindung des WebService herzustellen. sun.net.NetworkClient öffnet einen Standart-Socket mit einem Default-Konstruktor (String host, int port). Die lokale IP wird nicht mitgegeben und damit Standardmäßig auf 0.0.0.0 gesetzt. Das führt dann dazu, das auf dem Servern die primäre IP verwendet wird.

Komplexes Problem, werde ich wohl so schnell keine Antwort finden…

Gruß B.

Hi Bonkers,

Wenn man sich den Verlauf des Ganzen anschaut, landet man
irgendwann bei dem sun.net.NetworkClient des JDK, diese Klasse
wird verwendet um die Verbindung des WebService herzustellen.
sun.net.NetworkClient öffnet einen Standart-Socket mit einem
Default-Konstruktor (String host, int port). Die lokale IP
wird nicht mitgegeben und damit Standardmäßig auf 0.0.0.0
gesetzt. Das führt dann dazu, das auf dem Servern die primäre
IP verwendet wird.

wenn du schon so tief reindebugt hast, könntest du noch etwas präziser werden. Also z.B. welches Modul genau verwendet sun.net.NetworkClient und wie? Also wo vermutest du den Bug (und scheint mir ein Bug zu sein)?

Mir ist das wichtig, weil wir (ein genossenschaftlicher Webhoster) JBoss Hosting, für kleine Anwendungen auch im shared-Hosting, anbieten wollen. Und im shared Hosting ist Bindung auf 0.0.0.0 eben nicht akzeptabel. D.h. wir sind dann ggf. schon zwei, die das Problem lösen wollen :wink:

Alles Gute wünscht
… Michael

wenn du schon so tief reindebugt hast, könntest du noch etwas
präziser werden. Also z.B. welches Modul genau verwendet
sun.net.NetworkClient und wie?

Das stammt nicht aus meiner Feder, jemand anderes hat das bei uns herausgefunden, hier noch ein paar details:

  • JBossWS verwendet JBoss Remoting als Aufruf-Layer beim Verbindungsaufbau
  • Verwendet wird automatisch HTTP-Transport, welches wiederum mit dem HTTPClientInvoker registriert ist (org.jboss.remoting.InvokerRegistry).
    http-Transport daher, weil der WebService mit http://… startet.
  • Der HTTPInvokerClient (org.jboss.remoting.transport.http.HttpClientInvoker) verwendet intern die Standard HttpUrlConnection des JDK.
  • diese wiederum verwendet den sun.net.NetworkClient, der wiederum einen Standard-Socket öffnet…

Also wo vermutest du den Bug
(und scheint mir ein Bug zu sein)?

Ich denke nicht das es ein Bug ist, da es schon seit Urzeiten in Java so gehandelt wird, vielleicht eher ein Bug in JBoss. Auf jboss.org habe ich mir das noch nicht genauer angeschaut. Bei uns wird geprüft, ob das auf Basis des OS oder mit der Netzwerkkonfig zu beheben ist, aber ich glaubs nicht.

Viele Grüße

Bonkers

Hi nochmal,

  • Der HTTPInvokerClient
    (org.jboss.remoting.transport.http.HttpClientInvoker)
    verwendet intern die Standard HttpUrlConnection des JDK.
  • diese wiederum verwendet den sun.net.NetworkClient, der
    wiederum einen Standard-Socket öffnet…

Also wo vermutest du den Bug
(und scheint mir ein Bug zu sein)?

Ich denke nicht das es ein Bug ist, da es schon seit Urzeiten
in Java so gehandelt wird, vielleicht eher ein Bug in JBoss.

Deswegen fragte ich nach. Danke auch erstmal für die weiteren Infos.

Alles Gute wünscht
… Michael

Hi,

kann man dem JBoss AppServer Node nicht eine eigene IP Adresse zuweisen?
Beim SAP Netweaver AppServer geht das, und eine schnelle Suche im Internet hat das her ausgespuckt:
https://rhstack.108.redhat.com/articles/2006/09/18/l…

Wenn der AppServer die gewünschte IP hat sollte das Problem doch gelöst sein, oder habe ich da was falsch verstanden?

Gruss,
Herb

Hi Herb,

kann man dem JBoss AppServer Node nicht eine eigene IP Adresse
zuweisen?

eigentlich schon (für alles mögliche). Ich hatte es so verstanden, dass die genau an der Stelle verloren geht. JBoss mit Webservices habe ich noch keine Erfahrung, nur EJB.

Alles Gute wünscht
… Michael

das ist richtig man kann dem JBoss an eine IP binden. Das ist dann die jboss.bind.adress, jedoch versagt eben das ganze an genau dieser Stelle. Das heißt die anderen JBoss Aktionen gehen über diese Bindadresse, nur diese eine Komponente nicht.