IP
层的封装
C++
通用框架的设计
作者:
naven
1
IP
层封装介绍
TCP/UDP
是网络编程的基础技术,分别代表面向连接的稳定的网络通信技术和非连接方式的广播形式的网络通信技术,它们都建立在
IP
层之上,所以
IP
层的封装尤为重要。
IP
层的封装接口主要包括
DNS
的查询、
IP
地址和域名的互查、本地
IP
及名字的查询等,目前
IP
层使用的主要实现技术是
IPv4
,但是未来会慢慢升级到容量更大的
IPv6
,所以
IP
层的封装需要要同时支持这两种协议。操作系统实现它们都是通过增加新的
API
以及新的地址结构实现的,开发者编写跨协议的网络应用需要编写较复杂的程序来区分
IPv4
和
IPv6
协议,优秀的
ACE
框架则通过条件编译来支持
IPv6
,好像不能同时在程序中使用
IPv4
和
IPv6
协议。本
C++
框架参考
Java
的
InetAddress
及相关类实现了类似跨协议的
IP
层封装,编写网络应用基本不用考虑两种协议的不同,应为它们对外的接口类都是
InetAddress
,另外同时提供了与
Java
一样简单的域名和
IP
地址互查的接口,使用非常容易。
主要有如下一些类
class AbstraceInetAddress IP
地址的抽象类,定义
IP
类的方法
class InetAddress
表示
IP
地址的接口类
class Inet4Address
表示
IPv4
协议的
IP
地址实现类
class Inet6Address
表示
IPv6
协议的
IP
地址实现类
class SocketAddress
表示以域名
/IP/PORT
标识的网络地址
Abstract
类
class InetSocketAddress
表示以域名
/IP/PORT
标识的网络地址实现类
class NameService
内部使用的访问域名服务的类
对于
IP
寻址,有如下几个类:
InetAddress
、
Inet4Address
和
Inet6Address
。
IPv4
的实现类
Inet4AddressImpl
使用一个
32
位的
unsignednumber
标识,一个
IPv4
地址形式为
nnn.nnn.nnn.nnn
,其中
n
为一个整数,例于
129.250.35.250
。而
IPv6
的实现类
Inet6AddressImpl
使用一个
128
位的
unsigned number
标识,形式如同
x:x:x:x:x:x:x:x
,其中
x
表示一个十六进制的数字,例于
1080:0:0:0:8:800:200C:417A
。
对于
Socket
寻址,有如下两个类:
SocketAddress
和
InetSocketAddress
。其中
SocketAddress
是一个
abstract
的
socket
地址,不依赖于一个特定的协议,它提供给实现特定协议的子类来使用。
InetSocketAddress
是
SocketAddress
的一个子类,它表示一个
IP
类的
socket
地址,包括一个
IP
地址(如
129.250.35.250
)和端口(如
80
),或者包括一个域名(如
coastnews.com
)和一个端口(如
1000
),或者仅仅包括一个端口(如
1010
)。
2
Hello World!
下面的程序示例如何用上面的类进行
IP
查询操作:
void
main()
{
//
定义IP地址数组以存储IP地址列表
InetAddressArray iaa;
//
调用InetAddress方法获取此域名的所有IP,并存放到数组iaa中
int
cnt
=
InetAddress::getAllByName(
"
www.google.com
"
, iaa);
//
定义数组的迭代器
InetAddressArray::iterator it(iaa);
//
遍历数组,列出所有IP地址
while
(
!
it.done() )
{
//
输出IP地址的字符串形式
printf(
"
%s\n
"
, it
->
getHostAddress().c_str());
//
迭代器向前移一位
it.advance();
}
}
程序输出如下:
66.102.7.99
66.102.7.104
66.102.7.147
3
AbstractInetAddress
类
此类定义一个
Internet Procotol
(
IP
)地址类的接口,类定义如下:
class
AbstractInetAddress
{
protected
:
/**/
/*
*
* Specify the address family: Internet Protocol, Version 4
* @since 1.4
*/
const
static
int
IPv4
=
INET_FAMILY_IPV4;
/**/
/*
*
* Specify the address family: Internet Protocol, Version 6
* @since 1.4
*/
const
static
int
IPv6
=
INET_FAMILY_IPV6;
/**/
/*
*
* Specify address family preference
*/
static
BOOL preferIPv6Address;
/**/
/*
*
* Used to store the name service provider
*/
static
NameService
&
nameService;
/**/
/*
*
* Used to pointer to the actual address implemented
*/
static
InetAddressImplAutoPtr _impl;
virtual
InetAddressAutoPtr clone()
=
0
;
public
:
/**/
/*
*
* Destructor must implemented.
*/
virtual
~
AbstractInetAddress()
{}
/**/
/*
*
* Utility routine to check this InetAddress.
*/
virtual
BOOL isMulticastAddress()
=
0
;
virtual
BOOL isAnyLocalAddress()
=
0
;
virtual
BOOL isLoopbackAddress()
=
0
;
virtual
BOOL isLinkLocalAddress()
=
0
;
virtual
BOOL isSiteLocalAddress()
=
0
;
virtual
BOOL isMCGlobal()
=
0
;
virtual
BOOL isMCNodeLocal()
=
0
;
virtual
BOOL isMCLinkLocal()
=
0
;
virtual
BOOL isMCSiteLocal()
=
0
;
virtual
BOOL isMCOrgLocal()
=
0
;
virtual
SockAddr getAddress()
=
0
;
virtual
String getHostAddress()
=
0
;
virtual
BOOL isUnresolved()
=
0
;
/**/
/*
*
* Compares this object against the specified object.
* The result is <code>true</code> if and only if the argument is
* not <code>null</code> and it represents the same IP address as
* this object.
* <p>
* Two instances of <code>InetAddress</code> represent the same IP
* address if the length of the byte arrays returned by
* <code>getAddress</code> is the same for both, and each of the
* array components is the same for the byte arrays.
*
* @param obj the object to compare against.
* @return <code>true</code> if the objects are the same;
* <code>false</code> otherwise.
*/
BOOL equals(AbstractInetAddress
&
addr)
{
return
getAddress()
==
addr.getAddress()
?
TRUE : FALSE;
}
}
;
4
InetAddress
类
此类实现一个
Internet Procotol
(
IP
)地址,一个
IP
地址即可以是用一个
32-bit
的
unsigned
数来表示,也可以用一个
128-bit
的
unsgined
数来表示,它内部实现了一个底层的协议如
UDP
和
TCP
协议。
IP
地址的结构定义在
RFC790 Assigned Numbers
:
http://www.ietf.org/rfc/rfc790.txt
,
RFC1918 Address Allocation for Private Internets
:
http://www.ietf.org/rfc/rfc1918.txt
,
RFC2365 Administratively Scoped IP Multicast
:
http://www.ietf.org/rfc/rfc2365.txt
,
RFC2373 Version 6 Addressing Architecture
:
http://www.ietf.org/rfc/rfc2373.txt
。一个
InetAddress
的实体由一个
IP
地址和一个可能它通讯的
host name
组成,这个
host name
依赖于它是否使用一个
host name
来构造,或者它是否已经做了
host name
决议的倒装(
reverse host name resolution
)。
InetAddress
类的定义如下:
class
InetAdress :
public
AbstractInetAddress
{
/**/
/*
*
* @serial
*/
String _hostName;
/**/
/*
*
* Specifies the address family type, for instance, '1' for IPv4
* addresses, and '2' for IPv6 addresses.
*
* @serial
*/
int
_family;
/**/
/*
*
* Used to store the best available hostname
*/
String _canonicalHostName;
/**/
/*
*
* Used to pointer to the actual address object
*/
InetAddressAutoPtr _ia;
}
;
5
NameService
类
这个类实现一个名字服务的接口供
InetAddress
查询
IP
及其协议类型使用,它有如下一些方法:
class
NameService
{
/**/
/*
*
* The gethostbyaddr function retrieves the host information
* corresponding to a network address.
*
* Note The gethostbyaddr function has been deprecated by the
* introduction of the getnameinfo function. Developers creating
* socket applications are urged to use the getnameinfo function
* instead of the gethostbyaddr function. See Remarks.
*/
int
getHostByAddr(SockAddr
&
addr, String
&
host);
/**/
/*
*
* Lookup hostname in name service. If
* found return address and return count, -1 if not found.
*
* @param host host name to lookup
* @param addrs return the address list that found
* @return found addresses count, -1 if not found or error.
*/
int
lookupAllHostAddr(
const
String
&
host, InetAddressArray
&
addrs);
/**/
/*
*
* Lookup host port with service name and protocol in name service. If
* found return port and return count, -1 if not found.
*
* @param service host service to lookup
* @param protocol service protocol, default is "TCP"
* @return host port, -1 if not found or error.
*/
int
lookupHostPort(
const
String
&
service,
const
String
&
protocol
=
"
TCP
"
);
}
;
6
Inet4Adress
类
这个类实现一个
Internet Protocol version 4 (IPv4)
协议地址,定义在:
RFC790 Assigned Numbers
:
http://www.ietf.org/rfc/rfc790.txt
,
RFC1918 Address Allocation for Private Internets
:
http://www.ietf.org/rfc/rfc1918.txt
,
RFC2365 Administratively Scoped IP Multicast
:
http://www.ietf.org/rfc/rfc2365.txt
。一个
IPv4
地址的文本表示法使用如下一个格式输入:
d.d.d.d
d.d.d
d.d
d
当四个部分都被指定后,每一个会被解释为一个
assigned
字节的数据,从左到右,附值给一个四个字节的
IPv4
地址。此类的定义如下:
class
Inet4Adress :
public
AbstractInetAddress
{
/**/
/*
*
* Holds a 32-bit IPv4 address.
*
* @serial
*/
int
_address;
}
;
7
Inet6Adress
类
这个类实现一个
Internet Protocol version 6 (IPv6)
协议地址,定义在:
RFC2373 IP Version 6 Addressing Architecture
:
http://www.ietf.org/rfc/rfc2373.txt
。一个
IPv6
地址的文本表示法使用如下一个格式输入:
首选的格式是
x:x:x:x:x:x:x:x
,其中所有的“
x
”是表示地址的
8
个
16-bit
块的
16
进制数值,这是一个完整的格式,举例如下:
1080:0:0:0:8:800:200C:417A
需要注意的是以
0
开始的栏位是无必要写的,然而在每个栏位中必需有一个数字,除了如下描述的情形外:
由于一些分配了确定类型的
IPv6
地址的方法,它将为地址容纳一个长的
zero bit
的
strings
所共有,为了使得写这些包含了
zero bit
的地址更容易,所以使用
“
::
”来表示多个连续为
0
的栏位组,而“
::
”在一个地址中只能出现一次,“
::
”还能被用来压缩一个地址中以
0
开始到
0
结尾的栏位组,例如:
1080::8:800:200C:417A
一种可替换的格式在有时候更为便利,但处理混合有
IPv4
和
IPv6
协议的节点如
x:x:x:x:x:x:d.d.d.d
,其中所有的“
x
”表示地址的
6
个
high-order 16-bit
部分的十六进制数,而其它的“
d
”表示标准的
IPv4
地址的
4
个
low-order 8-bit
部分的十进制数,例如:
::FFFF:129.144.52.38
::129.144.52.38
其中“
::FFFF:d.d.d.d
”和“
::d.d.d.d
”分别地表示一个
IPv4-mapped IPv6
地址和一个
IPv4-compatible IPv6
地址,需要注意的是
IPv4
部分必须是
“
d.d.d.d
”格式,以下的格式都是不正确的:
::FFFF:d.d.d
::FFFF:d.d
::d.d.d
::d.d
但是下面的格式却是合法的:
::FFFF:d
然而它是一个表示
IPv4-compatible IPv6
地址的非传统的表示格式,
::255.255.0.d
其中的“
::d
”符合一个通常的
IPv6
地址,如
0:0:0:0:0:0:0:d
对于一个需要返回文本格式的地址的方法,
Inet6Address
将返回完整的格式,因为它是非常明确的,当在与其他文本数据结合使用的时候。
Inet6Address
类定义如下:
class
Inet6Adress :
public
AbstractInetAddress
{
/**/
/*
*
* cached scope_id - for link-local address use only.
*/
int
_cached_scope_id;
/**/
/*
*
* Holds a 128-bit (16 bytes) IPv6 address.
*
*/
int
_ipaddress[
16
];
}
;
8
SocketAddress
类和
InetSocketAddress
类
SocketAddress
类实现了一个不与任何一种协议绑定的
Socket
地址,它是一个抽象类,这表明必须使用它的绑定了特定协议的子类来表示
Socket
地址的实现。它为
sockets
的
binding
和
connecting
或者
return values
提供一个不可变的对象。
InetSocketAddress
类实现了一个
IP Socket Address
(即一个
IP address
加一个
port
端口),它还能是一对
host name
加一个
port
端口,此时会尝试去查找确定
host name
的实际地址。它的定义如下所示:
class
InetSocketAddress :
public
SocketAddress
{
/**/
/*
*
* The hostname of the Socket Address
* @serial
*/
String _hostname;
/**/
/*
*
* The IP address of the Socket Address
* @serial
*/
InetAddress _addr;
/**/
/*
*
* The port number of the Socket Address
* @serial
*/
int
_port;
}
;
C++
通用框架的设计
作者:
naven
日期:
2006-3-19