IPv6的基本认识
Posted looktheworld
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了IPv6的基本认识相关的知识,希望对你有一定的参考价值。
IPv6
1.IPv6的基本认识
IPv4 位数是 32位,4字节,能够提供的IP地址大约是42亿,但你知道的,如今一个人都不止一个IP地址,看看如今设备的数量及发展速度就知道,所以有了IPv6,IPv6能够提供大量的地址.
-
IPv6:128位,16字节,能够提供的IP地址听老师以及教材都谈及过,地球上的沙子都不放过,我也是佩服
具体是多少个IP地址呢?
-
2128 个可提供的IP地址,这大概是3.4*1038 这么多.
IPv6的地址格式
- IPv6使用128位地址,相比IPv4的32位地址更大。IPv6地址采用冒号分隔的八组四位十六进制数表示,如2001:0db8:85a3:0000:0000:8a2e:0370:7334。
- IPv6地址中可以使用双冒号(::)表示一组或多组连续的0,以简化地址表示。例如,2001:0db8::1 表示 2001:0db8:0000:0000:0000:0000:0000:0001。
2.IPv6的头部格式
- 对比IPv4:取消了校验和字段,本身IP校验和字段其实感觉就挺鸡驻的.现在大部分路由器都不怎么启用这个功能了,同时还取消了分片机制这个字段,还有可选字段也取消了.
嗯,其余字段作用功能基本一样,现在来理解一下
- 通行量号(Traffic Class):8bit,和IPv4的服务分区(TOS)差不多一个意思,用于表述数据包的一些流量要求,具体一点就是什么吞吐量,延迟的
- 流标号(Flow Label):20bit,这个字段属于特有的了,用于标识特定的数据流,通常用于实现服务质量(QoS)和流量标记。(这个字段可用可不用,这是我查到的资料,根据操作系统,网络设备也有关)
- 有效数据长度(Payload Length):16比特,标识数据部分的长度,单位字节.和IPv4的总长度字段功能基本一直.
- 下一个首部(Next Heade):8bit,这个字段表示紧随IPv6报文的下一个协议是什么,比如TCP,UDP,ICMPv6,和IPv4协议字段中的协议字段基本功能一样.
- 跳数限制(Hop Limit):8bit,这个字段表示网络上经过的最大跳数,就跟IPv4中的TTL字段一样,每经过一跳,跳数-1.
3.IPv6地址的结构
在IPv6地址中,还可以有一些特殊用途的地址类型:
- 单播地址(Unicast Address):用于标识单个接口的地址。
- 多播地址(Multicast Address):用于标识一组接口,数据包将被复制并发送到该组中的所有接口。
- 任播地址(Anycast Address):用于标识一组接口中的任意一个接口,数据包将被路由到距离最近的任一接口。
此外,还有保留地址用于特定的用途,如环回地址(Loopback Address)和链路本地地址(Link-Local Address)等。
回环地址 | 0000...0(128位) | ::1/128 |
---|---|---|
本地地址 | 1111110..... | FC00::/7 |
链路本地地址 | 1111111010... | FE80::/10 |
多播地址 | 11111..... | FF00::/8 |
-
本地地址:相当于IPv4中的内网的私有地址,可在一个网络下通信,因为在一个子网下,比如什么192.168.X开头的,还是10.开头的,能明白大致含义就行.
-
单播地址:上述表格里没有,也就是除了这些地址外的地址,相当于IPv4中的公网地址,也就是不会重复,其实这些地址的用法上和IPv4的用法上本质是一样的.
-
多播地址:相当于IPv4的组播地址.在IPv4中,组播地址一般都用D类地址来表示,也就是224.0.0.0开头,而IPv6也有这种表示.
-
链路本地地址:在IPv4中其实没有明确的规定,在同一链路单播通信,不经过路由器就可以使用链路本地地址.
链路与内网:个人感觉其实是对内网的一个细分,因为内网的主机随着IPv6的地址分配,其实这个内网下的主机数就变多了,这时候如果没有层次的划分,内网里的主机数也会显得很臃肿,链路就好比每个房间里的人都共用一个链路,或者说一栋楼里的都用一个链路,链路一般的划分是根据交换机,数据链路层嘛,这样一个链路的人们一般来说一定是一个内网下的人.
-
回环地址:127.0.0.1.....这个就不多说了.
接下来放出大图:方便理解链路本地与本地地址的区别:
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的基本认识的主要内容,如果未能解决你的问题,请参考以下文章