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

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM