字节转换和填充函数

    网络编程中,为保证发送协议栈和接收协议栈就如 32 位 IPv4 地址等多字节字段各个分节的传送顺序一致,经常需要在主机字节序和网络字节序之间进行转换。这种转换就是利用以下 4 个函数来实现的。
#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;
}

猜你喜欢

转载自aisxyz.iteye.com/blog/2387969