IPv6基本编程——Java

Posted AntarcticPenguin

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了IPv6基本编程——Java相关的知识,希望对你有一定的参考价值。

IPv6编程——Java

1. Java支持IPv6

Java 从 1.4 版开始支持 Linux 和Solaris 平台上的 IPv6。1.5 版起又加入了 Windows 平台上的支持。
在 IPv6 的环境下开发 Java 应用,或者移植已有的 IPv4 环境下开发的Java 应用到 IPv6 环境中来,对于 IPv6 网络地址的验证是必须的步骤,尤其是对那些提供了 UI(用户接口)的 Java 应用。

2. 获取本机IPv6地址

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

package com.text;

import java.io.IOException;
import java.net.Inet6Address;
import java.net.InetAddress;
import java.net.NetworkInterface;
import java.util.Enumeration;

public class Get_IPv6 {

    public static void main(String[] args) throws IOException{
        String str = getLocalIPv6Address();
        System.out.println(str);
    }

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


    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 标准地址的一部分,可以去除。

3. IPv4/IPv6 双环境下,网络的选择和测试

Java 提供了 InetAddress 的两个扩展类以供使用:Inet4Address 和 Inet6Address,其中封装了对于 IPv4 和 IPv6 的特殊属性和行为。然而由于Java 的多态特性,使得程序员一般只需要使用父类 InetAddress,Java 虚拟机可以根据所封装的 IP 地址类型的不同,在运行时选择正确的行为逻辑。所以在多数情况下,程序员并不需要精确控制所使用的类型及其行为,一切交给 Java虚拟机即可。具体的新增类型及其新增方法,请具体参阅 Sun 公司的 JavaDoc。
在 IPv4/IPv6 双环境中,对于使用 Java 开发的网络应用,比较值得注意的是以下两个 IPv6 相关的 Java 虚拟机系统属性。
java.net.preferIPv4Stack=<\true|false>
java.net.preferIPv6Addresses=<\true|false>
程序中设置代码如下:
System.setProperty("java.net.preferIPv6Addresses","true");
preferIPv4Stack(默认 false)表示如果存在 IPv4 和 IPv6 双栈,Java 程序是否优先使用 IPv4 套接字。默认值是优先使用 IPv6 套接字,因为 IPv6 套接字可以与对应的 IPv4 或 IPv6 主机进行对话;相反如果优先使用 IPv4,则只不能与 IPv6 主机进行通信。
preferIPv6Addresses(默认 false)表示在查询本地或远端 IP 地址时,如果存在 IPv4 和 IPv6 双地址,Java 程序是否优先返回 IPv6 地址。Java 默认返回 IPv4 地址主要是为了向后兼容,以支持旧有的 IPv4 验证逻辑,以及旧有的仅支持 IPv4 地址的服务。

4. socket 支持 IPv6 通信示例

客户端:

package com.text2;

import java.io.BufferedReader;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.Inet6Address;
import java.net.InetAddress;
import java.net.NetworkInterface;
import java.net.Socket;
import java.util.Enumeration;

public class Click {
    public static void main(String[] args) throws IOException {
        //获取本机IPv6地址getLocalIPv6Address()方法在上面已经提到
        String servernameString = getLocalIPv6Address();
        System.out.println("客户端启动…");
        System.out.println("当接收到服务器端字符为 \"OK\" 的时候, 客户端将终止\n");
        while (true) {
            Socket socket = null;
            try {
                // 创建一个流套接字并将其连接到指定主机上的指定端口号
                socket = new Socket(servernameString, 8080);
                // 读取服务器端数据
                DataInputStream input = new DataInputStream(socket.getInputStream());
                // 向服务器端发送数据
                DataOutputStream out = new DataOutputStream(socket.getOutputStream());
                System.out.print("请输入: \t");
                String str = new BufferedReader(new InputStreamReader(System.in)).readLine();
                out.writeUTF(str);
                String ret = input.readUTF();
                System.out.println("服务器端返回过来的是: " + ret);
                if ("OK".equals(ret)) {
                    System.out.println("客户端将关闭连接");
                    Thread.sleep(500);
                    break;
                }
                out.close();
                input.close();
            } catch (Exception e) {
                System.out.println("客户端异常:" + e.getMessage());
            } finally {
                if (socket != null) {
                    try {
                        socket.close();
                    } catch (IOException e) {
                        socket = null;
                        System.out.println("客户端 finally 异常:" + e.getMessage());
                    }
                }
            }
        }
    }
    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;
    }

    private static boolean isReservedAddr(InetAddress inetAddr) {
        if (inetAddr.isAnyLocalAddress() || inetAddr.isLinkLocalAddress()|| inetAddr.isLoopbackAddress()) {
            return true;
            }        
        return false;
    }
}

服务器端:

package com.text2;

import java.io.BufferedReader;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.InputStreamReader;
import java.net.ServerSocket;
import java.net.Socket;

public class Server {
    public static void main(String[] args) {
        // TODO Auto-generated method stub
        System.out.println("服务器启动…\n");
        Server server = new Server();
        server.init();
    }

    public void init() {
        try {
            ServerSocket serverSocket = new ServerSocket(8080);
            while (true) {
                // 一旦有堵塞, 则表示服务器与客户端获得了连接
                Socket client = serverSocket.accept();
                // 处理这次连接
                new HandlerThread(client);
            }
        } catch (Exception e) {
            System.out.println("服务器异常: " + e.getMessage());
        }
    }

    private class HandlerThread implements Runnable {
        private Socket socket;

        public HandlerThread(Socket client) {
            socket = client;
            new Thread(this).start();
        }

        public void run() {
            try {
                // 读取客户端数据
                DataInputStream input = new DataInputStream(socket.getInputStream());
                String clientInputStr = input.readUTF();
                //这里要注意和客户端输出流的写方法对应,否则会抛 EOFException
                // 处理客户端数据
                System.out.println("客户端发过来的内容:" + clientInputStr);
                // 向客户端回复信息
                DataOutputStream out = new DataOutputStream(socket.getOutputStream());
                System.out.print("请输入:\t");
                // 发送键盘输入的一行
                String s = new BufferedReader(new InputStreamReader(System.in)).readLine();
                out.writeUTF(s);
                out.close();
                input.close();
            } catch (Exception e) {
                    System.out.println("服务器 run 异常: " + e.getMessage());
            } finally {
                if (socket != null) {
                    try {
                        socket.close();
                    } catch (Exception e) {
                        socket = null;
                        System.out.println("服务端 finally 异常:" + e.getMessage());
                    }
                }
            }
        }
    }
}

<wiz_tmp_tag id="wiz-table-range-border" contenteditable="false" style="display: none;">

以上是关于IPv6基本编程——Java的主要内容,如果未能解决你的问题,请参考以下文章

第4章 基本tcp套接字编程

用Java压缩成IPV6地址

递进迭代敏捷

片段中的 super.onCreateView

VSCode自定义代码片段——JS中的面向对象编程

Twitter Kit RestAPI不支持IPv6? - 拒绝了Apple