扫一扫
关注微信公众号

Apache源代码全景分析:网络地址处理 (1)
2006-06-14   

在了解APR中对IP地址的封装之前,我们首先看一下通常情况下对IP地址的使用情况。下面的代码掩饰了简单的服务器端套接字的地址初始化过程:

struct sockaddr_in server_addr; /* 本机地址信息 */

server_addr.sin_family=AF_INET;

server_addr.sin_port=htons(SERVPORT);

server_addr.sin_addr.s_addr = INADDR_ANY;

bzero(&(server_addr.sin_zero),8);

……

bind(sockfd, (struct sockaddr *)&my_addr, sizeof(struct sockaddr));

  accept(sockfd, (struct sockaddr *)&remote_addr, &sin_size);

Socket API中提供了三种类型的地址:sockaddr,sockaddr_in和sockaddr_un。sockaddr是通用的套接字结构,sockaddr_in为Internet协议族的地址描述结构,sockaddr_un则是Unix协议组的地址描述结构。sockaddr_in结构中的sa_family决定是sockaddr_in还是sockaddr_un。

如果直接使用Socket API提供的地址结构,则至少存在下面的几个问题:

1、在网络应用程序中,对于internet地址,如上面的程序代码所示,通常总是使用sockaddr_in描述,而在一些Socket API函数中则使用sockaddr作为套接字地址,因此在使用的时候必须将sockaddr_in强制转换为sockaddr类型,这是一个麻烦而且容易出错的地方。

2、sockaddr_in也不是一个特别容易理解的数据结构。通常情况下,sin_family和sin_port相对容易记忆,而套接字地址sin_addr.s_addr则未必。套接字的这种结构对一般人而言无疑是一种噩梦。

3、另外一个问题则是Ipv6的地址问题。目前,Apache已经开始同时支持Ipv4和Ipv6两种类型的地址,如果用户需要支持Ipv6,则还必须使用Ipv6对应的地址数据结构。

对于一个良好的类库,不管是Ipv4还是Ipv6协议,都必须提供同样的接口,这种接口必须简单易懂,同时必须尽可能的隐藏内部的细节,比如对于sin_addr.s_addr无非暴露给用户。

基于上面的分析,APR中只使用一种数据结构apr_sockaddr_t来描述IP地址,该结构定义在文件apr_network_io.h中:

struct apr_sockaddr_t {

apr_pool_t *pool;

/*第一部分*/

char *hostname;

char *servname;

/*第二部分*/

apr_port_t port;

apr_int32_t family;

union {

struct sockaddr_in sin;

#if APR_HAVE_IPV6

struct sockaddr_in6 sin6;

#endif

#if APR_HAVE_SA_STORAGE

struct sockaddr_storage sas;

#endif

} sa;

/*第三部分*/

apr_socklen_t salen;

int ipaddr_len;

int addr_str_len;

void *ipaddr_ptr;

apr_sockaddr_t *next;

};

该结构描述了socket地址的三部分的信息内容:

第一部分:

Hostname是该地址对应的主机名称,而servname则是对应端口的服务名称,比如80对应的名称为”www”,21端口对应的servname则是”FTP”。如果某个端口比如9889并没有对应某个众所皆知的服务,那么servname则直接是端口的字符串描述。

第二部分:

该部分则对应的是sockaddr结构中的内容,port是端口,family则是地址协议族类型,包括AF_INET,AF_UNIX等。sa则为联合类型,用以描述对应的套接字地址,或者是Ipv4类型,或者是Ipv6类型,两者只能居其一。

第三部分:

这部分主要是一些与套接字地址相关的附加信息。Salen是当前套接字地址的长度,通常情况下它的值为sizeof(struct sockaddr_in),对于IPV6,则是sizeof(struct sockaddr_in6);ipiaddr_len则是对应得IP地址结构的长度,对于Ipv4总是sizeof(struct in_addr),而对于Ipv6,则是sizeof(struct in6_addr);addr_str_len则是IP地址缓冲的长度,对于Ipv4,该值为14,而对于IPV6,则是46。这三个地址的含义完全不同。

Ipaddr_ptr指针指向sockaddr结构中的IP地址结构,通常情况下,它的初始化使用下面的代码:

apr_socketaddr_t addr;

addr->ipaddr_ptr = &(addr->sa.sin.sin_addr);

对于一些服务器而言,可能会使用多个IP地址。这些IP地址之间通过next指针形成单链表结构。

从next可以看出各个socket地址之间可以形成链表。

9.1.2子网掩码结构

与此同时,APR中也定义了数据结构apr_ipsubnet_t来描述IP地址掩码,当然由于IP地址分为Ipv4和Ipv6,因此掩码描述也可以分为两种,apr_ipsubnet_t结构定义在文件apr_sockaddr.c中,属于内部数据结构,具体如下:

struct apr_ipsubnet_t {

int family;

#if APR_HAVE_IPV6

apr_uint32_t sub[4]; /* big enough for IPv4 and IPv6 addresses */

apr_uint32_t mask[4];

#else

apr_uint32_t sub[1];

apr_uint32_t mask[1];

#endif

};

family是当前掩码所属于的地址族,APR_INET表示Ipv4,而APR_INET6则表示Ipv6。

对于Ipv4而言,该结构演变为如下:

struct apr_ipsubnet_t {

int family;

apr_uint32_t sub[1];

apr_uint32_t mask[1];

};

而对于Ipv6,则该结构可以演变为如下:

struct apr_ipsubnet_t {

int family;

apr_uint32_t sub[4]; /* big enough for IPv4 and IPv6 addresses */

apr_uint32_t mask[4];

};

9.1.3 Socket地址处理接口

为了处理Socket地址,APR中提供了四个操作接口,这些接口定义在apr_network_io.h中,而实现则sockaddr.c中。这四个接口分别是:

9.1.3.1地址获取

由于APR中仅仅使用apr_sockaddr_t结构描述套接字地址,因此其余的各类描述信息最终都要转换为该结构,APR中提供apr_sockaddr_info_get函数实现该功能:

APR_DECLARE(apr_status_t) apr_sockaddr_info_get(apr_sockaddr_t **sa,

const char *hostname,

apr_int32_t family,

apr_port_t port,

apr_int32_t flags,

apr_pool_t *p);

该函数允许从主机名hostname,地址协议族family和端口port创建新的apr_sockaddr_t地址,并由sa返回。

hostname参数允许是实际的主机名称,或者也可以是字符串类型的IP地址,比如”127.0.0.1”,甚至可以是NULL,此时默认的地址是”0.0.0.0”。

family的值可以是AF_INET,AF_UNIX等系统定义类型,也可以是APR_UNSPEC类型,此时,地址协议族由系统决定。

flags参数用以指定Ipv4和Ipv6处理的 优先级,它的取值包括两种:APR_IPV4_ADDR_OK和APR_IPV6_ADDR_OK。这两个标志并不是在所有的情况下都有效,这可以从函数的实现中看出它的用法:

{

apr_int32_t masked;

*sa = NULL;

if ((masked = flags & (APR_IPV4_ADDR_OK | APR_IPV6_ADDR_OK))) {

if (!hostname ||

family != APR_UNSPEC ||

masked == (APR_IPV4_ADDR_OK | APR_IPV6_ADDR_OK)) {

return APR_EINVAL;u

}

#if !APR_HAVE_IPV6

if (flags & APR_IPV6_ADDR_OK) {

return APR_ENOTIMPL;

}

#endif

}

#if !APR_HAVE_IPV6

if (family == APR_UNSPEC) {

family = APR_INET; v

}

#endif

return find_addresses(sa, hostname, family, port, flags, p); w

}

热词搜索:

上一篇:Linux用户也能玩转Google Earth了
下一篇:Apache源代码全景分析:网络地址处理 (2)

分享到: 收藏