2016-08-04 45 views
-1

Ich bin eine Java-Anwendung zu schreiben, die eine Socket öffnet, wie folgt:In welchen Fällen wird Socket.getLocalAddress() ein IPv6 im Gegensatz zu einem IPv4 sein?

socketConnection = new Socket(); 
socketConnection.connect(new InetSocketAddress(server, port)); 

und führt gelegentlich Operationen wie Lesen und Schreiben. Eine Funktion, die wichtig ist, um Metadaten zusammen mit Paketen zu senden, sind die lokalen und entfernten Adressen der Verbindung. Zum Beispiel, erhalte ich die lokale Adresse Bytes als:

public byte[] getLocalIP() { 
     InetAddress localAddr = socketConnection.getLocalAddress(); 
     byte[] addressBytes = localAddr.getAddress(); 
     return addressBytes; 
    } 

Mein Protokoll möchte die IPv4 des Senders zusammen im Header gesendet haben. Manchmal gibt diese Funktion jedoch 16 statt 4 Bytes zurück, was Probleme verursacht. Noch verwirrender, ändert sich das Verhalten manchmal innerhalb desselben Laufs des Programms, trotz des gleichen Socket-Objekts, das IPv4 für vorherige Aufrufe zurückgibt. Es ist schwierig zu replizieren, ich bin mir immer noch nicht sicher, unter welchen Umständen es passiert.

Unter welchen Umständen wird das obige ein IPv6 statt eines IPv4 zurückgeben? Ist das abhängig von dem Netzwerk, auf dem ich arbeite? Und was würde dazu führen, dass es sich mitten in einer Programmausführung verschiebt?

+1

Es sollte ziemlich offensichtlich sein, welche Bedingung das verursacht - Sie erstellen einen IPv6-Socket, der an eine IPv6-Adresse gebunden ist (mit 'Inet6Address'), anstatt einen IPv4-Socket zu erstellen, der an eine IPv4-Adresse gebunden ist (mit' Inet4Address')).Wie erstellst und konfigurierst du 'socketConnection'? Ist diese Client-Seite oder Server-Seite? Bitte geben Sie ein [minimales, vollständiges und verifizierbares Beispiel] (http://stackoverflow.com/help/mcve) an. Wenn Ihr Protokoll keine IPv6-basierten Metadaten unterstützt, erstellen Sie zunächst keinen IPv6-Socket. –

+0

Ich habe ein Beispiel hinzugefügt, wie ich die Verbindung initialisiere. Ich habe auch klargestellt, dass das selbe "Socket" -Objekt während einer einzelnen Ausführung des Programms manchmal 4 Bytes gegenüber 16 Bytes zurückgibt, nachdem das Objekt initialisiert und verbunden wurde. –

+1

Es ist * PHYSISCH UNMÖGLICH * für einen verbundenen 'Socket', sowohl IPv4- als auch IPv6-Adressen zurückzugeben oder verschiedene IPv4/IPv6-Adressen zu unterschiedlichen Zeiten zurückzugeben. Es kann immer nur an eine Adresse gebunden sein, und diese Adresse ist für die Lebensdauer der Verbindung persistent. Wenn Sie unterschiedliche Ergebnisse sehen, müssen Sie etwas falsch in Ihrem Code tun, aber Sie haben kein [minimales, vollständiges und überprüfbares Beispiel] (http://stackoverflow.com/help/mcve) zur Diagnose bereitgestellt Was könnte das sein? –

Antwort

0

Sie erstellen ein Socket Objekt ohne es lokal zu einer IPv4 oder IPv6-Adresse ausdrücklich als verbindlich, so wird es schaffen und bindet ein internes IPv4 oder IPv6-Sockets an, ob Sie connect() auf den Remote-Server eine IPv4 oder IPv6-Adresse.

Wenn server ein InetAddress ist, wird die IP-Version auf der Grundlage, ob Sie eine Inet4Address oder Inet6Address verwenden. Aber

wenn server ein string ist, hat es zu einem InetAddress gelöst werden, und dass die Auflösung wird davon abhängen, ob die string enthält eine wörtliche IPv4-Adresse, eine wörtliche IPv6-Adresse oder einen Hostnamen. Im Fall von Literaladressen ist die Antwort offensichtlich - eine literale IPv4-Adresse wird in eine Inet4Address aufgelöst und eine literale IPv6-Adresse wird in eine Inet6Address aufgelöst. Aber eine hostname kann in beide Richtungen gehen, abhängig vom Ergebnis der DNS-Lookups.

Wenn server ist ein Hostname, und Sie wollen nur das Ergebnis zu IPv4 beschränken, rufen InetAddress.getAllByName() direkt und Schleife durch das resultierende Array (ein Hostname mehrere IP-Adressen zugewiesen werden können), connect() ‚ing nur Inet4Address Adressen bis einer von ihnen erfolgreich ist:

+0

Okay, aber warum sollte 'getLocalAddress()' zwischen einem IPv4 und einem IPv6 für das gleiche 'Socket'-Objekt wechseln, nachdem ich bereits verbunden und Nachrichten gesendet/empfangen habe? Dies ist das Verhalten, für das ich eine Erklärung suche. –

+1

Was Sie beschreiben, ist physikalisch unmöglich. Die IP-Version wird beim Erstellen des Socket-Deskriptors eingerichtet und kann die IP-Version danach nicht ändern. Es kann nur an lokale/Remote-IP-Adressen gebunden werden, die seiner festgelegten IP-Version entsprechen, und ein gebundener Socket kann während seiner Lebensdauer nicht von einer IP-Adresse zu einer anderen springen. Nach der Verbindung sind 'getLocalAddress()' und 'getInetAddress()' für die gesamte Lebensdauer der Verbindung persistent. Es passiert also etwas anderes in deinem Code. Meine Vermutung ist, dass Sie 'getLocalAddress()' wahrscheinlich auf verschiedenen 'Socket'-Objekten aufrufen und es nicht merken. –

+0

Ich nenne es definitiv nicht auf verschiedenen Sockets, und das Verhalten tritt definitiv auf. Ich erstelle nur einen 'Socket' und rufe nur' connect' einmal auf. Wenn es zu einer anderen IP-Adresse zurückspringt, geschieht dies intern in Java-Bibliothekscode, unabhängig von meinen Aufrufen. Dies ist das Verhalten, für das ich eine Erklärung suche. –

0

Ich konnte feststellen, was das Problem verursacht, obwohl ich immer noch nicht sicher bin, was genau unter den Abdeckungen passiert. Wenn die Verbindung serverseitig unterbrochen wird und Lesevorgänge aus dem Socket zu Fehlern Broken pipe führen, tritt dieses Verhalten auf. Siehe unten, wie ich es repliziert:

Socket s = new Socket(); 
    String server = "1.2.3.4" // not my real server ip 
    s.connect(new InetSocketAddress(server, 9050)); 
    try{ 
     System.out.println(s.getLocalAddress().getAddress().length); 
     byte[] badData = new byte[100]; 
     // server application will kill the connection when it cant parse this 
     // according to application logic 
     Arrays.fill(badData, (byte) 0xFF); 
     for(int i = 0; i < 1000; i++){ 
      OutputStream out = s.getOutputStream(); 
      out.write(badData); 
      out.flush(); 
      Thread.sleep(1); 
     } 

    } 
    catch(SocketException e){ 
     System.out.println(e.getMessage()); 
     System.out.println(s.getLocalAddress().getAddress().length); 
    } 

Welche gibt die folgenden:

4 
Broken pipe 
16 

Also, ich habe herausgefunden, was es verursacht wird, aber immer noch keine Einblicke in warum dieses Verhalten auftritt von Java.

+0

Die lokalen und Remote-Adressen eines Sockets sind bedeutungslos, nachdem die Verbindung unterbrochen wurde. Was du machst macht keinen Sinn. – EJP

+0

Einverstanden, dass sie bedeutungslos sind, aber es verursachte immer noch Fehler in meiner Anwendungslogik, als ich versuchte, meine Daten zu senden, bevor ich erkannte, dass die Verbindung von der Serverseite unterbrochen wurde. Deshalb fragte ich, was das Verhalten verursachen könnte. –