概述
目前运行主流的IT系统中,用于解决分布式系统内部模块及不同的系统间通信的一种主要的解决方案就是使用套接字Socket来开发应用。由于当前大部分正在运行的IT系统中使用套接字Socket开发环境基本上都是基于IPv4完成的,因此在IT系统由IPv4向IPv6演进方案中如何完成这部分相关应用的演进就显得尤为的关键,下面本文将从技术角度分别从编程API接口的差异性、为实现IPv6重构软件的关键技术及如何支持IPv4及IPv6双栈完成演进等这几个方面进行分析,给出完整的解决方案
1. 基于SOCKET技术的接口协议
通常用BSD Socket API (Windows平台用Win Socket API)作为基础开发应用协议。以下是IT系统常用的接口协议:
基于SOCKET技术的接口协议通常以SOKET API作为为基础开发应用协议,下表是IT系统常用的接口协议:
序号
1
2
3
4
5
6
Telnet协议是TCP/IP协议族中的一员,是Internet远程登陆服务的标准协议。应用Telnet协议能够把本地用户所使用的计算机变成远程主机系统的一个终端。它提供了三种基本服务:
(1)
(2)
(3)
SSH的英文全称是Secure Shell。通过使用SSH,你可以把所有传输的数据进行加密,这样“中间人”这种攻击方式就不可能实现了,而且也能够防止DNS和IP欺骗。还有一个额外的好处就是传输的数据是经过压缩的,所以可以加快传输的速度。 SSH有很多功能,它既可以代替telnet,又可以为ftp、pop、甚至ppp提供一个安全的“通道”。
从客户端来看,SSH提供两种级别的安全验证: 第一种级别(基于口令的安全验证)只要你知道自己帐号和口令,就可以登录到远程主机。所有传输的数据都会被加密,但是不能保证你正在连接的服务器就是你想连接的服务器。可能会有别的服务器在冒充真正的服务器, 也就是受到“中间人”这种方式的攻击。第二种级别(基于密匙的安全验证)需要依靠密匙,也就是你必须为自己创建一对密匙,并把公用密匙放在需要访问的服务器上。如果你要连接到SSH服务器上,客户端软件就会向服务器发出请求,请求用你的密匙进行安全验证。服务器收到请求之后,先在你在该服务器的HOME目录下寻找你的公用密匙,然后把它和你发送过来的公用密匙进行比较。如果两个密匙一致,服务器就用公用密匙加密“质询”(challenge)并把它发送给客户端软件。客户端软件收到“质询”(CHAP)之后就可以用你的私人密匙解密再把它发送给服务器。用这种方式,你必须知道自己密匙的口令。但是,与第一种级别相比,第二种级别不需要在网络上传送口令。第二种级别不仅加密所有传送的数据,而且“中间人”这种攻击方式也是不可能的(因为他没有你的私人密匙)。
FTP是TCP/IP协议组中的协议之一,它工作在TCP模型的应用层之上,使用TCP传输,FTP需要两个端口,一个端口是作为控制连接端口,也就是21端口,用于发送指令给服务器端以及等待服务器端的响应;另一个端口是数据传输端口,端口号20(仅PORT模式),是用来建立数据传输通道的。
TFTP全称为Trivial File Transfer Protocol,中文名叫简单文件传输协议。大家可以从它的名称上看出,它适合传送“简单”的文件。与FTP不同的是,它使用的是UDP的69端口,因此它可以穿越许多防火墙。不过它也有缺点,比如传送不可靠、没有密码验证等。虽然如此,它还是非常适合传送小型文件的。
SNMP(Simple Network Management Protocol,简单网络管理协议)的前身是简单网关监控协议(SGMP),用来对通信 <http://baike.baidu.com/view/15007.htm>线路进行管理。随后,人们对SGMP进行了很大的修改,特别是加入了符合Internet <http://baike.baidu.com/view/11165.htm>定义的SMI和MIB <http://baike.baidu.com/view/141513.htm>:体系结构,改进后的协议就是著名的SNMP。SNMP的目标是管理互联网 <http://baike.baidu.com/view/6825.htm>Internet上众多厂家生产的软硬件 <http://baike.baidu.com/view/25278.htm>平台,因此SNMP受Internet标准网络管理框架的影响也很大。现在SNMP已经出到第三个版本的协议 <http://baike.baidu.com/view/36190.htm>,其功能较以前已经大大地加强和改进了。
SNMP的体系结构是围绕着以下四个概念和目标进行设计的:保持管理代理(Agent)的软件成本尽可能低;最大限度地保持远程管理的功能,以便充分利用Internet的网络资源 <http://baike.baidu.com/view/8439.htm>;体系结构 <http://baike.baidu.com/view/1188494.htm>必须有扩充的余地;保持SNMP的独立性 <http://baike.baidu.com/view/562176.htm>,不依赖于具体的计算机 <http://baike.baidu.com/view/3314.htm>、网关 <http://baike.baidu.com/view/807.htm>和网络传输协议 <http://baike.baidu.com/view/16807.htm>。
2. SOCKET API接口的差异性:
基于套接字Socket API所开发的应用中,基本上都使用相同的编程模型,且所有通信的基本操作如connect、accept、listen、send/sendto、read/readfrom等都是通过Socket API函数来完成的。这一点无论在IPv4的网络环境下还是IPv6的网络环境下基本上都是一致的,这就保证了基于套接字Socket开发的应用软件在软件结构上基本没有变化,IPv4向IPv6演进所带来的变化主要集中在那些与地址相关的API函数上(包括地址有关的数据结构体)。RFC2553针对IPv6带来的套接字Socket API函数上的变化有明确的定义,IPv4和IPv6体现在套接字Socket API函数级别的差异可以用下面的表格来表示:
映射项
常量定义
IP地址结构体
地址
地址-表达式转换函数
名字-地址转换函数
那些使用C/C++等语言开发的应用软件基本上主要关注这些API函数及结构体的变化就可以了。
JAVA作为主要编程语言的情况:
Java是目前IT系统广泛使用的程序设计语言,在JDK的java.net包中(包括javax.net包)提供了完整的对套接字Socket编程的类定义,使用Java语言开发的应用都是基于这些类来完成的。JDK 从1.4版本开始部分支持IPv6协议,到了JDK1.5、JDK1.6就完全支持IPv6协议栈。现网调研的结果表明大多的IT系统都是可以运行在JDK1.5或以上的版本中。因此我们可以认为目前我们使用的JDK就已经具备了IPv6的能力。在JDK中和IPv4及IPv6相关的类只有两个:java.net.Inet4Address和java.net.Inet6Address, 也就是说如果要区分IPv4和IPv6的话,通过区分这两个类就可以了,而且这两个类均继承自同一个父类java.net.InetAddress,在JDK中,和套接字Socket相关的其他所有的类都仅仅与这个父类java.net.InetAddress相关(都应用的该类),而与IPv4(java.net.Inet4Address)及IPv6(java.net.Inet6Address)没有直接的关系,即这些类对于是IPv4还是IPv6是透明的,因此从套接字Socket API接口这个层面上来看没有差异。
3. 软件重构涉及到的主要技术
通过上面的分析,我们知道了IPv4向IPv6演进所带来的套接字Socket API接口上的差异,在了解及关注这些差异的基础上,我们通过一些关键技术点的分析帮助我们重构软件代码以实现向IPv6的演进。
在使用C/C++语言来开发的Socket软件中,主要关注以下几个技术关键点:
3.1 地址结构的变化
IPv4环境下,通常使用的地址结构体sockaddr_in在头文件中定义如下:
struct sockaddr_in{
};
struct in_addr{
};
面向IPv4编程的socket编程中,针对上述地址结构体的赋值example如下:
rcv_udp_addr.sin_family = AF_INET;
rcv_udp_addr.sin_addr.s_addr = htonl(INADDR_ANY);
rcv_udp_addr.sin_port = htons(UDPRCV_PORT);
IPv6环境下,地址结构体的发生对应的变化,定义如下:
struct sockaddr_in6 {
};
struct in6_addr {
};
相对的对于IPv6地址结构体的赋值如下:
rcv_udp_addr.sin6_family = PF_INET;
rcv_udp_addr.sin6_addr.s6_addr =in6addr_any;
rcv_udp_addr.sin6_prot = htons (UDPRCV_PORT);
需要注意的是地址在IPv4中的地址结构体中sin_addr是主机字节顺序(经过htons()函数转换),
而在IPv6中,直接使用的是网络字节顺序。
3.2 socket的创建
建立socket的连接的API函数定义原型如下:
int socket(int domain,int type,int protocol);
对于IPv4而言,domain=AF_INET, 而IPv6,则domain=PF_INET6
例如创建TCP的Socket语句如下:
sock_tcp_ipv4 = socket(AF_INET,SOCK_STREAM,0);
sock_tcp_ipv6 = socket(PF_INET6,SOCK_STREAM,0);
3.3 字符串地址和网络顺序IP地址的相互转换
在IPv4环境中,使用int inet_aton(const char *cp, struct in_addr *np)来完成从字符串地址到IP地址的转换,在IPv6环境中,使用int inet_pton(int Af,const char *src,void *dst),多一个输入参数,使用样例:inet_pton(AF_INET6,hostname,&snd_tcp_addr.sin6_addr);
反之,当由网络顺序的IP地址转换为字符串的时候,IPv4中,使用char *inet_ntoa(struct in_addr_in)而在IPv6环境中,使用const char* inet_ntop(int af, const void* src, char* dst, socklen_t cnt)这个函数,参数发生了明显的变化,使用样例:
inet_ntop(PF_INET6, &rcv_udp_addr_sin6_addr,ip,sizeof(ip));
3.4 主机名和地址的转换(域名解析)
在IPv4环境中,使用gethostbyname()或者gethostbyaddr()函数来实现转换;在IPv6环境中,则使用getnameinfo()或者getaddrinfo()函数来完成转换,需要包含以下的三个头文件:
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
3.5 组播地址常量
对于组播的应用,在IPv6环境下增加了一些常量的定义,在调用套接字Socket API相关函数的时候使用。代码重构的时候,关注的常量如下表所示:
IPV6_MULTICAST_IF
IPV6_MULTICAST_HOPS
IPV6_MULTICAST_LOOP
IPV6_JOIN_GROUP
IPV6_LEAVE_GROUP
rc=setsockopt(s,IPPROTO_IPV6,IPV6_MULTICAST_HOPS,(char )&gTtl,sizeof(gTt1));
以上的几个技术关键点适合于Unix、Linux平台下的C/C++语言来开发的Socket软件的重构。相对于Windows平台,以上几点基本上也是适合的(毕竟Winsock库还是基本兼容标准的Socket API定义的),但是相对于Windows平台在重构代码是需要多注意下面几点特殊性:
(1)
对于Windows平台而言,只有Winsock库在2.2以上才支持IPv6。
(2)
(3)
在现场调研的过程中,我们也发现了许多的IT系统在使用使用C/C++语言开发Socket软件的时候,并不是直接使用原始的套接字Socket API函数或在此基础上的封装的库来完成的,而是使用了第三方的库来完成的,这里我们对常用的用于网络编程第三方的库也进行相关的分析。
在Linux、Unix及 Windows平台下,ACE(The Adaptive Comunication Environment)是一个被广泛用来进行网络应用开发的软件包,ACE软件包从5.3版本开始就提供了对IPv6的支持,ACE软件包中提供的有关IP地址封装的类是ACE_INET_Addr,该类屏蔽了IP地址不同版本(IPv4、IPv6)的区别,相应类的成员方法也都兼容了不同地址版本,因此建立在ACE软件包基础上进行的网络应用的开发是不需要做这种区分的(输入的地址是IPv4的地址,则ACE_INET_Addr实例化的是具备IPv4特性的实例,输入地址是IPv6的地址,则ACE_INET_Addr实例化的是具备IPv6特性的实例), 在重构代码过程中重点需要关注的是当前ACE库是否具备了支持IPv6的能力,即检查编译ACE库的时候是否定有#ACE_HAS_IPV6宏定义。
相对于C/C++开发的Socket应用程序而言,使用Java程序设计语言开发的Socket应用程序在从IPv4环境重构到IPv6环境中基本上不需要做什么代码的重构,这主要取决于JDK目前的版本已经提供了对IPv6的支持且有关IP地址的差异(IPv4和IPv6)已经被屏蔽(上一节已经进行了分析JDK类库的情况),例如下面的代码:
Socket echosocket = new Socket(“hostip”,7);
如果”hostip”对应的是IPv4的地址,则echosocket 是IPv4类型的Socket,如果”hostip”对应的是IPv6的地址,则echosocket是IPv6类型,一条语句对于这两种地址类型都适应。
虽然JDK对于我们提供了很多的方便,使得我们的代码基本上不需要重构,但是还是有两个问题需要考虑:
1)
(1)
(2)
(3)
相对于其他的JVM(例如IBM的JVM、Oracle的JRockit等)需要检查相应的对主机操作系统的要求。
2)
虽然大部分Java开发的应用中都不用关心到底是用的是IPv4的地址还是IPv6的地址,在某些情况下,例如需要输出连接的IP地址信息或者需要获得地址信息的详细内容,这个时候是需要区分的。前面已经分析过java.net包中大部分类的成员函数使用的地址类是java.net.InetAddress,这个类是java.net.Inet4Address和java.net.Inet6Address类的父类,因此可以通过检查java.net.InetAddress的子类类型来确认到底地址使用的是IPv4的地址还是IPv6的地址,样例代码如下:
……… //相关的IPv4的地址操作
……..//相关的IPv6地址操作
1.1.1.1.
本文前面已经分析认为从IPv4向IPv6演进的最佳解决方案是IT系统提供对双栈应用的支撑,下面将对如何使得那些基于套接字Socket技术实现的接口支持双栈进行重点分析。
目前的IT系统大都是建立在操作系统上层的应用软件,要想应用系统支持IPv4及IPv6双栈,操作系统首先要支持双栈,因此在重构应用软件之前,确认或升级操作系统至支持双栈是必要条件。
前面已经分析过基于套接字Socket技术实现的接口都采用相同的编程模式,即典型的客户-服务器模式(Client-Server),针对于服务器端而言,双栈模式下服务器端Socket应该能够同时对绑定的IPv4地址及IPv6地址进行监听,从而完成对不同地址的服务请求。想要达到这一目的可以有以下几种方法:
1)
针对服务端而言,如果将服务端监听的地址绑定为”::”(IPv4中的0.0.0.0),将意味着服务器监听系统地址列表中的所有地址,即无论是IPv4的地址还是IPv6的地址都将被服务器监听以提供服务。例如下面的代码:
int port = 1099;
ServerSocket
Socket s ;
while (true) {
服务器端将监听地址列表中(无论IPv4地址还是IPv6地址)所有地址的1099端口,客户端无论访问的是IPv4的地址还是IPv6的地址,只要端口是1099,都将获得服务器端的服务。
显然,这种模式是一种最简单的重构模式。
2)
服务器端监听的IPv6地址是兼容地址(地址模式为::w.x.y.z)或者是IPv4映射的地址(地址模式为::ffff:w.x.y.z),那么在双栈模式下,服务器实际上是对两个地址进行监听,例如,服务器端绑定监听的IPv6地址是::ffff:192.158.112.8, 那么实际上服务器对”::ffff:192.158.112.8”这个IPv6的地址进行监听,同时也对192.158.112.8这个地址进行监听,客户端Socket访问的目标地址无论是192.158.112.8这个IPv4地址,还是访问”::ffff:192.158.112.8”这个IPv6的地址,服务端都可以提供服务。
3)
服务端需要监听的地址是两个完全独立的IPv4地址及IPv6地址,此时通过分别对两个不同的地址分别监听来完成双栈应用支撑,参见下面的样例代码:
ADDRINFO
ServerSocket[0] = socket(AF_INET6, SOCK_STREAM,PF_INET6);
}
显然,这种模式是一种比较复杂的重构模式。
最后
以上就是着急嚓茶为你收集整理的(转)网络编程:Socket编程从IPv4转向IPv6支持的全部内容,希望文章能够帮你解决(转)网络编程:Socket编程从IPv4转向IPv6支持所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
发表评论 取消回复