欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

字节转换和填充函数

程序员文章站 2022-07-12 16:48:47
...
    网络编程中,为保证发送协议栈和接收协议栈就如 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;
}