如何实现在一个 Socket 应用程序中同时支持 IPv4 和 IPv6

 我来答
邹辈酉
2016-10-21 · 超过38用户采纳过TA的回答
知道答主
回答量:68
采纳率:100%
帮助的人:13.5万
展开全部
实践证明它是健壮,易于实现,并具有很好的互操作性。但是 IPv4 协议的初始设计仍有一些未考虑到的地方,随着 Internet 的飞速发展和新型应用的不断涌现,这些不足逐渐显露出来。首先,近年来 Internet 成指数级数增长,而只有 32 位地址的 IPv4 引起了迫在眉睫的 IP 地址空间耗尽的问题;第二,IPv4 路由结构较为扁平,使得 Internet 上骨干路由器需要维护庞大的路由表;第三,目前 IPv4 实现方案中,多数情况需要手工配置或者使用 DHCP 有状态方式配置协议,随着越来越多的节点要求接入网络,需要一种简便的配置方式;第四,在 IP 级的安全方面,IPv4 并不强制使用 IPSec ;最后,IPv4 协议中使用服务类型 TOS 字段来支持实时通信流传送,而 TOS 功能有限,因此对实时数据传输 Qos 的支持不是很理想。为解决上述问题及其它相关问题,互联网工程任务组织 IETF 开发了一套新的协议和标准即 IP 版本 6(IPv6)。它吸纳了很多用于更新 IPv4 的新思想,在设计时力求对上下层协议造成最小的影响。与 IPv4 相比,IPv6 协议具有以下一些新的特性:IPv6 协议头采用了固定长度的头部,路由器在处理 IPv6 报头时,效率会更高。IPv6 具有 128 位的巨大地址空间,既便为当前所有主机都分配一个 IPv6 地址,IPv6 仍然有充足的地址供以后使用。IPv6 具有即插即用特性。 IPv6 引入了无状态地址自动配置方式,链路上的主机根据路由公告和自身的链路地址可以自动生产一个 IPv6 地址,从而简化了入网主机的配置过程,实现了 IPv6 的即插即用。提供网络层的认证和加密。 IPv6 支持 IPSec,这为网络安全提供了一种基于标准的解决方案。IPv6 更好地支持 QoS 。 IPv6 协议头部包含流标签字段,使得路由器可以对属于一个流的数据包进行识别和提供特殊处理;用业务流分类字段来区分通信流的优先级。因此 IPv6 对 QoS 提供了更好的支持。IPv6 更好地支持移动性。虽然 IPv4 也有移动特性,但是作为 IPv4 的扩展实现,受到体系结构和连通性的限制。而 IPv6 的移动特性是内置的,具有较少的局限性并具有更强的可伸缩性和健壮性,可满足将来 Internet 的通信需求。IPv6 具有很好的可扩展性,这可通过在 IPv6 协议头之后添加新的扩展协议头实现。回页首Java 对 IPv6 的支持Java 从 1.4 开始,已经提供了对 IPv6 的支持。 Java APIs 遵循了如下 IPv6 标准:RFC2373: IPv6 Addressing ArchitectureRFC2553: Basic Socket Interface Extensions for IPv6RFC2732: Format for Literal IPv6 Address in URL但是由于安全等因素,Java 并没有支持原始套接字。除此之外,一些 IPv6 特性,诸如隧道,自动配置,移动 IP 等,Java 都没有提供支持。与 C/C++ 对 IPv6 的支持不同,Java 对 IPv6 的支持是自动和透明的,也就是说现有的 Java 程序不需要经过修改就可以直接支持 IPv6 。以下面代码为例,这段代码在 IPv4 上可以正常运行,同样也可以工作在 IPv6 上。InetAddress ip = InetAddress.getByName("java.sun.com"); Socket s = new Socket(ip, 80);Java 对 IPv6 的支持体现在其 JDK 对 IPv6 的支持上,当然前提条件是操作系统需要提供对 IPv6 的支持。以下操作系统已经提供了对 IPv6 的支持,表1. OS 对 IPv6 的支持Windows:Windows 2000Windows XPWindows NTWindows 2003 serverLinux:Linux kernel 2.2 及以上版本Unix:AIX 4.3 及以上版本Solaris 8 及以上版本HP-UX 11i 及以上版本BSD/OS 4.0 及以上版本True64 Unix 4.0D 及以上版本FreeBSD 4.0 及以上版本NetBSDOpenBSD 2.7 及以上版本Other OS:OS/390, Z/OS V1R4 及以上版本OS400 V5R2 及以上版本Mac OS X下面列出了各个版本 JDK 对 IPv6 的支持情况。表2. JDK 对 IPv6 的支持JDK/OSWindowsLinuxAIXSolarisHPUXZOSIBM JDK 1.4NOYESYESNOIBM JDK 1.5YESYESYESYESSUN JDK 1.4NOYESYESSUN JDK 1.5YESYESYESJRockit JDK 1.4NOYESJRockit JDK 1.5YESYESHP-UX JDK 1.4YESHP-UX JDK 1.5YES回页首RMI 对 IPv6 的支持既然Java 对 IPv6 的支持是透明的,那么 RMI 程序理论上就应该同时支持 IPv4 和 IPv6,但测试结果告诉我们只有在 RMI 服务器端套接字不绑定 IP 地址的情况下这种结论才成立。考虑下面这样一个例子,一个支持双栈的服务器,同时配置了 IPv4 地址和 IPv6 地址。服务器应用程序用如下代码创建了一个服务器套接字,等待客户端的连接。由于 Java 对 IPv6 的透明支持,IPv4 和 IPv6 的客户端都可以正常连接到这台服务器上。清单1. 无 IP 绑定的服务器套接字程序 try { int port = 2000; ServerSocket srv = new ServerSocket(port); Socket socket = srv.accept(); } catch (IOException e) { e.printStackTrace(); }通过natestat – na可以看出,服务器监听在 0.0.0.0:2000 上,这样任何客户端都可以连接到服务器上,其中包括 IPv6 客户端用服务器的 IPv6 地址也能顺利连接。Proto Local Address Foreign Address State TCP 0.0.0.0:2000 0.0.0.0:0 LISTENING但是很多时候,应用程序从安全等角度考虑,常常需要将服务器套接字绑定在某个具体的 IP 上。现在我们假设把服务器套接字绑定在一个 IPv4 地址上,如下程序所示:清单2. 绑定 IP 的服务器套接字程序 try { int port = 2000; ServerSocket srv = new ServerSocket(port); srv.bind(new InetSocketAddress( “ 9.181.27.34 ” , port) Socket socket = srv.accept(); } catch (IOException e) { e.printStackTrace(); }通过natestat – na,我们可以发现监听方式已改变: Proto Local Address Foreign Address State TCP 9.181.27.34:2000 0.0.0.0:0 LISTENING从套接字的定义看出,它由 IP 和端口组成,它们唯一确定了一个套接字,同时也限定了访问套接字的方式。在访问由固定 IP 和端口组成的套接字时,客户端必须指定服务器的 IP 和端口才能正常连接。在服务器绑定 IPv4 地址的情况下,IPv6 客户端就无法用服务器的 IPv6 地址进行访问,当然 IPv4 客户端能通过服务器的 IPv4 地址 9.181.27.34 进行连接访问。上面分析了 Java 对 IPv6 的支持以及服务器套接字如何影响客户端的连接,接下来我们用实例分析 RMI 对 IPv6 的支持。我们搭建了如下的实验环境:图1. 实验环境接下来,我们设计了一个基本的 RMI 应用程序,下面这段是 RMI 服务器程序:清单3. RMI 服务器程序 import java.rmi.*; import java.rmi.server.*; import java.rmi.registry.*; public class SampleServerImpl extends UnicastRemoteObject implements SampleServer { SampleServerImpl() throws RemoteException { super(); } public int sum(int a,int b) throws RemoteException { return a + b; } public static void main(String args[]) { try { //create a local instance of the object SampleServerImpl Server = new SampleServerImpl(); //put the local instance in the registry Naming.rebind("rmi://9.181.27.34/SAMPLE-SERVER" , Server); System.out.println("Server waiting....."); } catch (java.net.MalformedURLException me) { System.out.println("Malformed URL: " + me.toString()); } catch (RemoteException re) { System.out.println("Remote exception: " + re.toString()); } } }编译源程序,启动rmiregistry,然后运行 RMI 服务器程序。通过netstat – na我们可以看见,RMI 监听方式如下,尽管我们在程序中用了”Naming.rebind("rmi://9.181.27.34/SAMPLE-SERVER" , Server)”:Proto Local Address Foreign Address State TCP 0.0.0.0:1099 0.0.0.0:0 LISTENING那么,根据我们上面关于套接字对客户端连接的影响的分析,我们可以看出在这种情况下,IPv4 RMI 客户端和 IPv6 RMI 客户端都应该能够顺利连接 RMI 服务器。下面我们就看一下 IPv4 客户端的连接程序:清单4. RMI 客户端程序 import java.rmi.*; import java.rmi.server.*; public class SampleClient { public static void main(String[] args) { //get the remote object from the registry try { //using RMI server ’ s IPv4 address to connect String url = "//9.181.27.34/SAMPLE-SERVER"; SampleServer remoteObject = (SampleServer)Naming.lookup(url); System.out.println("Got remote object"); System.out.println(" 1 + 2 = " + remoteObject.sum(1,2) ); } catch (RemoteException exc) { System.out.println("Error in lookup: " + exc.toString()); } catch (java.net.MalformedURLException exc) { System.out.println("Malformed URL: " + exc.toString()); } catch (java.rmi.NotBoundException exc) { System.out.println("NotBound: " + exc.toString()); } } }编译并运行该程序,可以看到正常连接到 RMI 服务器,运行结果如下:Got remote object 1 + 2 = 3下面我们在 IPv4 RMI 客户端程序的基础上作一下改动,用 RMI 服务器的 IPv6 地址进行连接,修改如下:String url = "//2001:251:1a05::6/SAMPLE-SERVER"; SampleServer remoteObject = (SampleServer)Naming.lookup(url);编译修改之后的程序,在 IPv6 RMI 客户端上运行,同样可以正常连接 RMI 服务器,运行结果如下:Got remote object 1 + 2 = 3通过这个例子,我们可以看出基本的 RMI 程序对 IPv6 的支持是透明的,它可以同时支持 IPv4 和 IPv6 。通过这个例子,我们也可以看出,它实际上不能绑定 IP 地址,这在安全性要求比较高的企业级应用程序中并不是很合适,它们通常采用UnicastRemoteObject类的exportObject(Remote obj, int port, RMIClientSocketFactory csf, RMIServerSocketFactory ssf)方法来绑定 RMI IP 地址。在RMIServerSocketFactory实例中,创建 RMI 服务器套接字并绑定 IP 地址,在RMIClientSocketFactory实例中通过服务器绑定的 IP 地址进行访问。在RMI 服务器绑定 IP 地址的情况,如果让 RMI 同时支持 IPv4 和 IPv6 呢?下面给出了一个解决方案。既然要让 RMI 同时支持 IPv4 和 IPv6,那么在服务器端,我们就要同时创建两个套接字,一个绑定在 IPv4 地址上,一个绑定在 IPv6 地址上。首先,我们应该创建两个类IPv4RMIServerSocket和IPv6RMIServerSocket,他们都实现RMIServerSocketFactory接口。IPv4RMIServerSocket创建一个服务器套接字并绑定在 RMI 服务器的 IPv4 地址上;IPv6RMIServerSocket创建一个服务器套接字并绑定在 RMI 服务器的 IPv6 地址上。其次,在创建两个类IPv4RMIClientSocket和IPv6RMIClientSocket,他们都实现RMIClientSocketFactory接口。IPv4RMIClientSocket创建一个客户端套接字并通过 RMI 服务器的 IPv4 地址进行连接;IPv6RMIClientSocket创建一个客户端套接字并通过 RMI 服务器的 IPv6 地址进行连接。然后,在创建好我们需要的服务器和客户端类之后,服务器应用程序需要两次调用exportObject方法将远程对象导出。但是有一个问题出现了,同一个远程对象不能同时导出两次。如何解决这个问题呢?办法就是我们需要对远程对象作一个wrapper。现在假设有一个远程对象Kernel的类定义如下:清单5. Kernel 类定义 public class Kernel extends Remote{ public void addWebServer(String hostName, int port) throws RemoteException { //Function implementation code } public void changeLogLevel(int level) throws RemoteException { //Function implementation code } }Kernel 的 Wrapper 定义如下:清单6. Kernel 的 Wrapper 类定义 public class KernelWrapper extends Remote { transient Kernel kernel_; public KernelWrapper (Kernel kernel) throws RemoteException, IOException { super(); kernel_ = kernel; } public void addWebServer(String hostName, int port) throws RemoteException { kernel_.addWebServer(hostName, port); } public void changeLogLevel(int level) throws RemoteException { kernel_.changeLogLevel(level); } }在应用程序初始化的时候,实例化一个 Kernel 实例,并将它作为参数实例化两个 KernelWrapper 实例,如下所示:清单7. KernelWrapper 实例化 kernelObj = new Kernel(); //remote kernel object for IPv4 clients ipv4kernelObj = new KernelWrapper (kernelObj); //remote kernel object for IPv6 clients ipv6kernelObj = new KernelWrapper (kernelObj);最后应用程序需要将 ipv4kernelObj 和 ipv6kernelObj 远程对象导出,如下所示:清单8. 远程对象导出 //export remote object for IPv4 client UnicastRemoteObject.exportObject( ipv4kernelObj, 1099, IPv4RMIClientSocket, IPv4RMIServerSocket ) //export remote object for IPv6 client UnicastRemoteObject.exportObject( ipv6kernelObj, 1099, IPv6RMIClientSocket, IPv6RMIServerSocket )这样IPv4 客户端通过服务器的 IPv4 地址进行访问,而 IPv6 客户端通过服务器的 IPv6 地址进行访问,从而成功的使得 RMI 服务器在绑定 IP 地址的情况下同时支持 IPv4 和 IPv6 。回页首结束语本文在分析服务器套接字对 IPv4 和 IPv6 客户端的影响的基础上,介绍了两种不同的 RMI 应用对 IPv6 的支持情况,同时给出了一种 RMI 服务器在需要绑定 IP 地址的情况下如何同时支持 IPv4 和 IPv6 客户端的解决方案。参考资料 IPv6 主页:IPv6 官方网站。
Networking IPv6 User Guide for J2SDK/JRE 1.4:JDK 1.4 对 IPv6 的支持。
An example of RMI programming:RMI 的简单例子。
“探索Internet Protocol, version 6 (IPv6)”(developerWorks,2006 年 7 月):了解 IPv6 的地址格式、主要优点和符合新标准的 IT 产品。
“配置FTP 服务器以支持 IPv6”(developerWorks,2006 年 8 月):在本文中,学习配置 File Transfer Protocol (FTP) 服务器以支持 IPv6,然后通过一个简单的使用 IPv6 地址的 Java 程序来与 FTP 服务器通信。
“结合使用 WebSphere Application Server V7 和 IPv6”(developerWorks,2008 年 11 月):本文将介绍用于验证 IBM WebSphere Application Server V7 的过程,以验证其对 IPv6 以及对 IPv4/IPv6 混合模式基础结构的支持。
developerWorks Java 技术专区:这里有数百篇关于 Java 编程的文章。
关于作者沈刚目前在 IBM Tivoli 部门从事 ITM for Applications 产品开发工作。关闭[x]关于报告滥用的帮助报告滥用谢谢! 此内容已经标识给管理员注意。关闭[x]关于报告滥用的帮助报告滥用报告滥用提交失败。 请稍后重试。关闭[x]developerWorks:登录IBM ID:需要一个 IBM ID?忘记IBM ID?密码:忘记密码?更改您的密码 保持登录。单击提交则表示您同意developerWorks 的条款和条件。 使用条款 当您初次登录到 developerWorks 时,将会为您创建一份概要信息。您在developerWorks 概要信息中选择公开的信息将公开显示给其他人,但您可以随时修改这些信息的显示状态。您的姓名(除非选择隐藏)和昵称将和您在 developerWorks 发布的内容一同显示。所有提交的信息确保安全。关闭[x]请选择您的昵称:当您初次登录到 developerWorks 时,将会为您创建一份概要信息,您需要指定一个昵称。您的昵称将和您在 developerWorks 发布的内容显示在一起。昵称长度在 3 至 31 个字符之间。 您的昵称在 developerWorks 社区中必须是唯一的,并且出于隐私保护的原因,不能是您的电子邮件地址。昵称:(长度在 3 至 31 个字符之间)单击提交则表示您同意developerWorks 的条款和条件。 使用条款. 所有提交的信息确保安全。为本文评分评论回页首
推荐律师服务: 若未解决您的问题,请您详细描述您的问题,通过百度律临进行免费专业咨询

为你推荐:

下载百度知道APP,抢鲜体验
使用百度知道APP,立即抢鲜体验。你的手机镜头里或许有别人想知道的答案。
扫描二维码下载
×

类别

我们会通过消息、邮箱等方式尽快将举报结果通知您。

说明

0/200

提交
取消

辅 助

模 式