技术开发 频道

使用Java开发兼容 IPv6 的网络应用程序

  如何正规化 IPv6 地址

  在网络程序开发中,经常使用 IP 地址来标识一个主机,例如记录终端用户的访问记录等。由于 IPv6 具有有零压缩地址等多种表示形式,因此直接使用 IPv6 地址作为标示符,可能会带来一些问题。为了避免这些问题,在使用 IPv6 地址之前,有必要将其正规化。除了通过我们熟知的正则表达式,笔者在开发过程中发现使用一个简单的 Java API 也可以达到相同的效果。

  清单 2. 正规化地址

InetAddress inetAddr = InetAddress.getByName(ipAddr);
ipAddr
= inetAddr.getHostAddress();
System.out.println(ipAddr);

  InetAddress.getByName(String) 方法接受的参数既可以是一个主机名,也可以是一个 IP 地址字符串。我们输入任一信息的合法 IPv6 地址,再通过 getHostAddress() 方法取出主机 IP 时,地址字符串 ipAddr 已经被转换为完整形式。例如输入 2002:97b:e7aa::97b:e7aa,上述代码执行过后,零压缩部分将被还原,ipAddr 变为 2002:97b:e7aa:0:0:0:97b:e7aa。

  如何获取本机 IPv6 地址

  有时为了能够注册 listener,开发人员需要使用本机的 IPv6 地址,这一地址不能简单得通过 InetAddress.getLocalhost() 获得。因为这样有可能获得诸如 0:0:0:0:0:0:0:1 这样的特殊地址。使用这样的地址,其他服务器将无法把通知发送到本机上,因此必须先进行过滤,选出确实可用的地址。以下代码实现了这一功能,思路是遍历网络接口的各个地址,直至找到符合要求的地址。

  清单 3. 获取本机 IP 地址

                
public static String getLocalIPv6Address() throws IOException {
    InetAddress inetAddress
= null;
    Enumeration
<NetworkInterface> networkInterfaces = NetworkInterface
        .getNetworkInterfaces();
    outer:
    
while (networkInterfaces.hasMoreElements()) {
        Enumeration
<InetAddress> inetAds = networkInterfaces.nextElement()
        .getInetAddresses();
        
while (inetAds.hasMoreElements()) {
            inetAddress
= inetAds.nextElement();
            
//Check if it's ipv6 address and reserved address
            if (inetAddress instanceof Inet6Address
                
&& !isReservedAddr(inetAddress)) {
                
break outer;
            }
        }
    }

    String ipAddr
= inetAddress.getHostAddress();
    
// Filter network card No
    int index = ipAddr.indexOf('%');
    
if (index > 0) {
        ipAddr
= ipAddr.substring(0, index);
    }

    
return ipAddr;
}

/**
* Check if it's "local address" or "link local address" or
* "loopbackaddress"
*
*
@param ip address
*
*
@return result
*/
private static boolean isReservedAddr(InetAddress inetAddr) {
    
if (inetAddr.isAnyLocalAddress() || inetAddr.isLinkLocalAddress()
        
|| inetAddr.isLoopbackAddress()) {
        
return true;
    }

    
return false;
}

  为了支持 IPv6,Java 中增加了两个 InetAddress 的子类:Inet4Address 和 Inet6Address。一般情况下这两个子类并不会被使用到,但是当我们需要分别处理不同的 IP 协议时就非常有用,在这我们根据 Inet6Address 来筛选地址。

  isReservedAddr() 方法过滤了本机特殊 IP 地址,包括“LocalAddress”,“LinkLocalAddress”和“LoopbackAddress”。读者可根据自己的需要修改过滤标准。

  另一个需要注意的地方是:在 windows 平台上,取得的 IPv6 地址后面可能跟了一个百分号加数字。这里的数字是本机网络适配器的编号。这个后缀并不是 IPv6 标准地址的一部分,可以去除。

0
相关文章