2016-08-04 184 views
-1

我写打开Socket一个Java应用程序,如下所示:在什么情况下,Socket.getLocalAddress()是IPv6还是IPv4?

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

偶尔如读取和写入执行操作。一个对发送元数据和数据包很重要的函数是连接的本地和远程地址。例如,我得到了本地地址字节:

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

我的协议,希望有在头一起发送发送端的IPv4。但是,有时这个函数返回16个字节而不是4个,这会导致问题。更令人困惑的是,尽管相同的Socket对象返回IPv4以前的调用,但程序在同一运行过程中有时会发生变化,即。这很难复制,我还不确定它在什么情况下发生。

在什么情况下上述情况会返回IPv6而不是IPv4?这是依赖于我运行的网络吗?在程序执行过程中会发生什么样的变化?

+1

应该很明显是什么原因导致这种情况 - 您正在创建绑定到IPv6地址的IPv6套接字(使用'Inet6Address'),而不是创建绑定到IPv4地址的IPv4套接字(使用'Inet4Address' )。你最初如何创建和配置'socketConnection'?这是客户端还是服务器端?请提供[最小,完整和可验证示例](http://stackoverflow.com/help/mcve)。如果您的协议不支持基于IPv6的元数据,则不要创建IPv6套接字来开始。 –

+0

我添加了一个如何初始化连接的例子。我还澄清说,在对象初始化并连接之后,在程序的单次运行期间,相同的'Socket'对象有时会返回4个字节与16个字节。 –

+1

对于连接的'Socket'来说,它是*物理上不可能的*返回IPv4和IPv6地址,或者在不同的时间返回不同的IPv4/IPv6地址。它一次只能绑定到一个地址,并且该地址在连接的整个生命周期中都是持久的。如果你看到不同的结果,你*有*在你的代码中做错了,但你没有提供[最小,完整和可验证的例子](http://stackoverflow.com/help/mcve)来诊断那可能是什么。 –

回答

0

您正在创建Socket对象,但未将其明确地绑定到IPv4或IPv6地址,因此它将根据您是否使用IPv4或IPv6地址为远程服务器connect()创建并绑定内部IPv4或IPv6套接字。

如果server是一个InetAddress,IP版本将基于您是否使用Inet4AddressInet6Address

但如果serverstring,它必须被解析为InetAddress,并且该决议将取决于string是否包含文字IPv4地址,文字的IPv6地址或主机名。在文字地址的情况下,答案很明显 - 文字IPv4地址将解析为Inet4Address,文字IPv6地址将解析为Inet6Address。但是hostname可以采取任何方式,这取决于DNS查找的结果。

如果server是主机名,并且要限制结果为IPv4,直接和循环调用InetAddress.getAllByName()向所得阵列(主机名可分配有多个IP地址),connect()“荷兰国际集团只Inet4Address地址直到其中一个成功:

socketConnection = null; 

for(InetAddress addr : InetAddress.getAllByName(server)) 
{ 
    if (addr instanceof Inet4Address) 
    { 
     socketConnection = new Socket(); 
     try { 
      socketConnection.connect(new InetSocketAddress(addr, port)); 
      break; 
     } 
     catch (IOException e) { 
      socketConnection = null; 
     } 
    } 
} 

if (socketConnection == null) { 
    throw new ConnectException("Cannot connect to '" + host + "' using IPv4"); 
} 
+0

好吧,但是为什么在我已经连接并且正在发送/接收消息之后,'getLocalAddress()'会在同一个'Socket'对象的IPv4和IPv6之间变化?这是我试图找到解释的行为。 –

+1

你所描述的实际上是不可能的。 IP版本是在创建套接字描述符时建立的,之后不能更改IP版本。它只能绑定到与其建立的IP版本匹配的本地/远程IP地址,并且绑定的套接字在其生命周期内不能从一个IP地址跳转到另一个IP地址。连接后,'getLocalAddress()'和'getInetAddress()'在连接的整个生命周期中都是持久的。因此,您的代码中正在发生其他事情。我的猜测是,你可能在不同的'Socket'对象上调用'getLocalAddress()'并且没有意识到它。 –

+0

我绝对不是在不同的套接字上调用它,并且行为肯定发生。我只创建一个'Socket',并且只调用'connect'一次。如果它反弹到一个不同的IP,那么它就发生在Java库代码的内部,与我的调用无关。这是我试图找到解释的行为。 –

0

我能确定是什么原因造成的问题,虽然我仍然不知道到底是什么在幕后发生的事情。如果服务器端的连接断开,并且从套接字读取的结果为Broken pipe错误,则会发生此行为。请参阅下面的我如何复制它:

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); 
    } 

,它输出以下内容:

4 
Broken pipe 
16 

所以,我已经想通了,是什么原因造成的,但仍然没有见解,为什么从Java出现这种情况。

+0

在连接中断后,套接字的本地和远程地址没有意义。你在做什么是没有意义的。 – EJP

+0

一致认为它们没有意义,但是在我意识到连接从服务器端断开之前,当我尝试格式化要发送的数据时,它仍然导致在我的应用程序逻辑中抛出错误。因此,我为什么要问什么会导致这种行为。 –

相关问题