网络编程中,为保证发送协议栈和接收协议栈就如 32 位 IPv4 地址等多字节字段各个分节的传送顺序一致,经常需要在主机字节序和网络字节序之间进行转换。这种转换就是利用以下 4 个函数来实现的。
在这些函数的名字中,h 代表 host,n 代表 network,s 代表 short,l 代表 long。当使用这些函数时,我们并不关心主机字节序和网络字节序的真实值(大端还是小端),只是调用适当的函数在主机和网络字节序之间转换某个给定值。在那些与网际协议所用字节序(大端)相同的系统中,这四个函数通常被定义为空宏。
操作多字节字段的函数有两组,它们既不对数据作解释,也不假设数据是以空字符结束的 C 字符串。在处理套接字地址结构时就经常需要这些类型的函数,因为诸如 IP 地址这样的字段中可能包含有值为 0 的字节,但却不是 C 字符串。
名字以 b(表示字节)开头的第一组函数起源于 4.2 BSD。名字以 mem(表示内存)开头的第二组函数起源于 ANSI C 标准。
bzero 把目标字节串中指定数目的字节置为 0。经常使用该函数来把一个套接字地址结构初始化为 0。bcopy 将指定数目的字节从源字节串移到目标字节串。bcmp 比较两个任意的字节串。
memset 把目标字节串指定数目的字节置为 c。memcpy 类似 bcopy,不过两个指针参数的顺序是相反的,而且当源字节串与目标字节串重叠时,bcopy 能够正确处理,但 memcpy 的操作结果却不可知(这种情形下必须改用 ANSI C 的 memmove 函数)。memcmp 比较两个任意的字节串。
下面介绍两组在 ASCII 字符串和网络字节序的二进制值之间转换网际地址的地址转换函数。
1、inet_aton、inet_addr(目前已废弃)和 inet_ntoa 在点分十进制和网络字节序间转换 IPv4 地址。
2、inet_pton 和 inet_ntop 对于 IPv4 和 IPv6 地址都适用。函数名中的 p 和 n 分别代表表达(presentation)和数值(numeric)。地址的表达格式通常是 ASCII 字符串,数值格式则是存放到套接字地址机构中的二进制值。
inet_aton 将 strptr 所指的 C 字符串转换成 32 位的网络字节序二进制值,并通过 addrptr 指针来存储。该函数有一个没写入正式文档中的特征:若 addrptr 指针为空,则函数仍然对输入的字符串执行有效性检查,但是不存储任何结果。
inet_ntoa 将一个 32 位的网络字节序二进制 IPv4 地址转换成相应的点分十进制。由该函数的返回值所指向的字符串驻留在静态内存中。这意味着该函数是不可重入的。
inte_pton 和 inet_ntop 的 family 参数既可以为 AF_INET,也可以为 AF_INET6,其它不被支持的值都将使这两个函数返回一个错误,并将 errno 置为 EAFNOSUPPORT。inet_pton 将 strptr 指针所指的字符串转换成二进制值,并通过 addrptr 指针存放。inet_ntop 进行相反的转换,从数值格式转换到表达格式。len 参数是目标存储单元的大小,以免该函数溢出其调用者的缓冲区。为有助于指定这个大小,在 <netinet/in.h> 中有如下定义:
#define INET_ADDRSTRLEN 16 // for IPv4 dotted-decimal
#define INET6_ADDRSTRLEN 46 // for IPv6 hex string
若 len 太小,不足以容纳表达格式结果(包括结尾的空字符),那么返回一个空指针,并将 errno 置为 ENOSPC。
inet_ntop 中的 strptr 参数不可以是一个空指针。调用者必须为目标存储单元分配内存并指定其大小。调用成功时,该指针就是该函数的返回值。
下面给出了只支持 IPv4 的 inet_pton 和 inet_ntop 函数的简单定义。
#include <netinet/in.h>
uint16_t htons(uint16_t host16bitvalue);
uint32_t htonl(uint32_t host32bitvalue);
/* 返回值:均为网络字节序的值 */
uint16_t ntohs(uint16_t net16bitvalue);
uint32_t ntohl(uint32_t net32bitvalue);
/* 返回值:均为主机字节序的值 */
在这些函数的名字中,h 代表 host,n 代表 network,s 代表 short,l 代表 long。当使用这些函数时,我们并不关心主机字节序和网络字节序的真实值(大端还是小端),只是调用适当的函数在主机和网络字节序之间转换某个给定值。在那些与网际协议所用字节序(大端)相同的系统中,这四个函数通常被定义为空宏。
操作多字节字段的函数有两组,它们既不对数据作解释,也不假设数据是以空字符结束的 C 字符串。在处理套接字地址结构时就经常需要这些类型的函数,因为诸如 IP 地址这样的字段中可能包含有值为 0 的字节,但却不是 C 字符串。
名字以 b(表示字节)开头的第一组函数起源于 4.2 BSD。名字以 mem(表示内存)开头的第二组函数起源于 ANSI C 标准。
#include <strings.h>
void bzero(void *dest, size_t nbytes);
void bcopy(const void *src, void *dest, size_t nbytes);
int bcmp(const void *ptr1, const void *ptr2, size_t nbytes);
/* 返回值:相等则为 0,否则为非 0 */
#include <string.h>
void *memset(void *dest, int c, size_t len);
void *memcpy(void *dest, const void *src, size_t nbytes);
int memcmp(const void *ptr1, const void *ptr2, size_t nbytes);
/* 返回值:ptr1==ptr2,返回 0;ptr1<ptr2,返回负数;否则返回正数 */
bzero 把目标字节串中指定数目的字节置为 0。经常使用该函数来把一个套接字地址结构初始化为 0。bcopy 将指定数目的字节从源字节串移到目标字节串。bcmp 比较两个任意的字节串。
memset 把目标字节串指定数目的字节置为 c。memcpy 类似 bcopy,不过两个指针参数的顺序是相反的,而且当源字节串与目标字节串重叠时,bcopy 能够正确处理,但 memcpy 的操作结果却不可知(这种情形下必须改用 ANSI C 的 memmove 函数)。memcmp 比较两个任意的字节串。
下面介绍两组在 ASCII 字符串和网络字节序的二进制值之间转换网际地址的地址转换函数。
1、inet_aton、inet_addr(目前已废弃)和 inet_ntoa 在点分十进制和网络字节序间转换 IPv4 地址。
2、inet_pton 和 inet_ntop 对于 IPv4 和 IPv6 地址都适用。函数名中的 p 和 n 分别代表表达(presentation)和数值(numeric)。地址的表达格式通常是 ASCII 字符串,数值格式则是存放到套接字地址机构中的二进制值。
#include <arpa/inet.h>
int inet_aton(const char *strptr, struct in_addr *addrptr);
/* 返回:若字符串有效则为 1,否则为 0 */
in_addr_t inet_addr(const char *strptr);
/* 返回:若字符串有效则为网络字节序的 IPv4 地址,否则为 INADDR_NONE */
char *inet_ntoa(struct in_addr inaddr);
/* 返回:指向一个点分十进制数串的指针 */
int inet_pton(int family, const char *strptr, void *addrptr);
/* 返回:若成功,则为 1;若输入不是有效的表达格式则为 0;若出错则为 -1 */
const char *inet_ntop(int family, const void *addrptr, char *strptr, size_t len);
/* 返回:若成功则为指向结果的指针,否则为 NULL */
inet_aton 将 strptr 所指的 C 字符串转换成 32 位的网络字节序二进制值,并通过 addrptr 指针来存储。该函数有一个没写入正式文档中的特征:若 addrptr 指针为空,则函数仍然对输入的字符串执行有效性检查,但是不存储任何结果。
inet_ntoa 将一个 32 位的网络字节序二进制 IPv4 地址转换成相应的点分十进制。由该函数的返回值所指向的字符串驻留在静态内存中。这意味着该函数是不可重入的。
inte_pton 和 inet_ntop 的 family 参数既可以为 AF_INET,也可以为 AF_INET6,其它不被支持的值都将使这两个函数返回一个错误,并将 errno 置为 EAFNOSUPPORT。inet_pton 将 strptr 指针所指的字符串转换成二进制值,并通过 addrptr 指针存放。inet_ntop 进行相反的转换,从数值格式转换到表达格式。len 参数是目标存储单元的大小,以免该函数溢出其调用者的缓冲区。为有助于指定这个大小,在 <netinet/in.h> 中有如下定义:
#define INET_ADDRSTRLEN 16 // for IPv4 dotted-decimal
#define INET6_ADDRSTRLEN 46 // for IPv6 hex string
若 len 太小,不足以容纳表达格式结果(包括结尾的空字符),那么返回一个空指针,并将 errno 置为 ENOSPC。
inet_ntop 中的 strptr 参数不可以是一个空指针。调用者必须为目标存储单元分配内存并指定其大小。调用成功时,该指针就是该函数的返回值。
下面给出了只支持 IPv4 的 inet_pton 和 inet_ntop 函数的简单定义。
int inet_pton(int family, const char *strptr, void *addrptr){
if(family == AF_INET){
struct in_addr in_val;
if(inet_aton(strptr, &in_val)){
memcpy(addrptr, &in_val, sizeof(struct in_addr));
return 1;
}
return 0;
}
errno = EAFNOSUPPORT;
return -1;
}
const char *inet_ntop(int family, const void *addrptr, char *strptr, size_t len){
const u_char *p = (const u_char *)addrptr;
if(family == AF_INET){
char temp[INET_ADDRSTRLEN];
snprintf(temp, sizeof(temp), "%d.%d.%d.%d", p[0], p[1], p[2], p[3]);
if(strlen(temp) >= len){
errno = ENOSPC;
return NULL;
}
strcpy(strptr, temp);
return strptr;
}
errno = EAFNOSUPPORT;
return NULL;
}