inet_pton函数和inet_ntop函数的用法及简单实现

本文介绍用于IPv4及IPv6地址转换的函数inet_pton与inet_ntop。inet_pton将地址从字符串形式转换为二进制形式,而inet_ntop则执行相反操作。文章还提供了适用于仅支持IPv4环境的简化实现。

        这两个函数是随IPv6出现的新函数,对于IPv4地址和IPv6地址都适用。函数名中的p和n非别代表表达(presentation)和数值(numeric)。地址的表达格式通常是ASCII字符串,数值格式则是存放到套接字地址结构中的二进制值。函数如下:

#include<arpa/inet.h>
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

        这两个函数的family参数既可以是AF_INET,也可以是AF_INET6。如果以不被支持的地址族作为family的参数,这两个函数就都返回一个错误,并将errno置为EAFNOSUPPORT。

第一个函数尝试转换由strptr指针所指的字符串,并通过addrptr指针存放二进制结果。若成功则返回1,否则如果对所指定的family而言输入的字符串不是有效的表达式,那么值为0。

        inet_ntop进行相反的转换,从数值格式(addrptr)转换到表达格式(strptr)。len参数是目标存储单元的大小,以免该函数溢出其调用者的缓冲区。为有助于指定这个大小,在<netinet/in.h>头文件中有如下定义:

#define INET_ADDRSTRLEN   16
#define INET6_ADDRSTRLEN 46

        如果len太小,不足以容纳表达式结果(包括结尾的空字符),那么返回一个空指针,并置errno为ENOSPC。

        inet_ntop函数的strptr参数不可以是一个空指针。调用者必须为目标存储单元分配内存并指定其大小。调用成功时,这个指针就是该函数的返回值。

        即使系统还不支持IPv6,也可以采取下列措施开始使用这些新函数,即用代码

inet_pton(AF_INET, cp, &foo.sin_addr);
代替代码
foo.sin_addr.s_addr = inet_addr(cp);
再用代码
char str[INET_ADDRSTRLEN];
ptr = inet_ntop(AF_INET, &foo.sin_addr, str, sizeof(str));
代替代码
ptr = inet_ntoa(foo.sin_addr);

下面给出只支持IPv4的inet_pton函数的简单定义和只支持IPv4的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 (0);
        }
    }
    errno = EAFNOSUPPORT;
    return (-1);
}

const char * inet_ntop(int family, const void *addrptr, char *strptr, size_t lne)
{
    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);
}


<think>好的,我现在需要详细解释inet_addrinet_pton这两个函数的功能、用法以及区别。根据用户提供的引用资料,我需要先整理每个函数的基本信息,然后比较它们的异同点,并注意用户提到的兼容性错误处理问题。首先,inet_addr函数。根据引用[1],用户遇到了编译错误,提示inet_addr已过时,建议使用inet_ptonInetPton代替。这说明inet_addr可能在新的代码中不被推荐使用,存在兼容性问题。引用[2]提到inet_addr比较宽松,但可能不符合严格的格式要求,而inet_pton可以进行更有效的判断。引用[3][4]也提到了inet_pton支持IPv4IPv6,而inet_addr只能处理IPv4地址。此外,引用[4]中的代码示例展示了inet_addr的使用方式,以及inet_ntoa函数的使用,这可能涉及到线程安全的问题。接下来,inet_pton函数。根据引用[3],这个函数可以将点分十进制转换为二进制形式,支持IPv4IPv6。引用[2]中的代码示例展示了如何使用inet_pton来验证IP地址的有效性,返回值为0表示格式错误,1表示成功,-1可能是协议族不支持的错误。这里需要说明函数的参数返回值。然后,两者的区别。功能上,inet_addr仅支持IPv4,而inet_pton支持IPv4IPv6。错误处理方面,inet_addr在无效地址时返回INADDR_NONE,而inet_pton通过返回值0表示无效格式。此外,线程安全方面,inet_ntoa是不可重入的,而inet_pton可能更安全。另外,inet_pton的引入是为了替代旧函数,提供更好的兼容性扩展性。在用法示例部分,需要分别展示两个函数的正确使用方法,包括参数传递错误检查。例如,inet_addr的使用示例,以及如何处理其返回值;inet_pton的参数结构,如何检查返回值是否为1,0或-1。同时,要注意到inet_pton需要指定地址族(AF_INET或AF_INET6),而inet_addr不需要,因为它只处理IPv4。还需要提到用户引用中提到的编译错误,即使用inet_addr时可能出现的C4996警告,解决方案是改用inet_pton或者定义特定的宏来禁用警告。但推荐使用inet_pton以符合新的标准。最后,生成相关问题,可能涉及其他转换函数inet_ntop,或者如何处理IPv6地址,或者这些函数在不同平台上的兼容性问题。现在需要组织这些信息,确保结构清晰,分点说明每个函数的功能、用法、区别,并附上代码示例注意事项。同时,正确引用用户提供的资料,比如在提到编译错误时引用[^1],在讨论错误处理时引用[^2]等。</think>### `inet_addr` 与 `inet_pton` 函数详解 #### 一、功能对比 | 特性 | `inet_addr` | `inet_pton` | |--------------------|--------------------------------------|--------------------------------------| | **协议支持** | 仅IPv4 | 支持IPv4IPv6 | | **错误处理** | 返回`INADDR_NONE`表示无效地址[^4] | 返回`0`表示无效格式,`1`表示成功[^2] | | **线程安全** | 不可重入(依赖静态存储) | 可重入 | | **头文件** | `<arpa/inet.h>`(Linux) | `<arpa/inet.h>`或`<ws2tcpip.h>`[^3] | | **兼容性** | 已过时(需特殊处理编译警告)[^1] | 推荐使用的新标准接口 | #### 二、函数原型与参数 ##### 1. `inet_addr` ```c #include <arpa/inet.h> in_addr_t inet_addr(const char *cp); // 输入点分十进制字符串,返回网络字节序的32位整数[^4] ``` - **返回值**:成功返回IPv4地址(网络字节序),失败返回`INADDR_NONE` - **限制**:无法处理`255.255.255.255`(返回`INADDR_NONE`) ##### 2. `inet_pton` ```c #include <arpa/inet.h> int inet_pton(int af, const char *src, void *dst); // 支持IPv4/IPv6的地址转换[^3] ``` | 参数 | 类型 | 说明 | |------|------------|-------------------------------| | `af` | `int` | 地址族(AF_INET或AF_INET6) | | `src`| `const char*` | 输入的点分十进制字符串 | | `dst`| `void*` | 输出的二进制地址缓冲区 | - **返回值**:`1`(成功)、`0`(格式错误)、`-1`(协议族错误)[^2] #### 三、典型用法示例 ##### 1. `inet_addr` 基础用法 ```c struct sockaddr_in addr; addr.sin_addr.s_addr = inet_addr("192.168.1.1"); // 将字符串转为网络字节序 if (addr.sin_addr.s_addr == INADDR_NONE) { // 错误检测[^4] perror("Invalid IP address"); } ``` ##### 2. `inet_pton` 安全用法 ```c struct in_addr ipv4; if (inet_pton(AF_INET, "10.0.0.1", &ipv4) <= 0) { // 严格校验地址格式[^2] if (errno == EAFNOSUPPORT) { fprintf(stderr, "Unsupported address family\n"); } else { fprintf(stderr, "Invalid address format\n"); } } ``` #### 四、核心区别解析 1. **协议扩展性** `inet_pton`通过`af`参数明确指定地址族类型,可无缝支持IPv6地址转换(使用`AF_INET6``struct in6_addr`)[^3] 2. **错误处理机制** ```c // inet_addr的错误检测 if (inet_addr("256.0.0.1") == INADDR_NONE) { // 检测到非法地址[^4] printf("Invalid address\n"); } // inet_pton的错误检测 if (inet_pton(AF_INET, "300.1.1.1", &ipv4) == 0) { // 明确区分格式错误[^2] printf("Format error\n"); } ``` 3. **字节序处理** - `inet_addr`直接返回网络字节序的32位整数 - `inet_pton`将结果存储在指定缓冲区,需配合地址结构体使用: ```c struct sockaddr_in6 ipv6_addr; inet_pton(AF_INET6, "2001:db8::1", &ipv6_addr.sin6_addr); // IPv6地址转换[^3] ``` #### 五、迁移实践建议 1. **替换`inet_addr`的编译警告处理** ```c #pragma warning(disable : 4996) // Windows下禁用警告[^1] #define _WINSOCK_DEPRECATED_NO_WARNINGS // 另一种处理方式[^1] ``` 2. **IPv4地址转换对照表** | 旧代码 | 新代码 | |---------------------------------|--------------------------------------------| | `inet_addr("127.0.0.1")` | `inet_pton(AF_INET, "127.0.0.1", &addr)` | | `addr.s_addr = INADDR_ANY` | `inet_pton(AF_INET, "0.0.0.0", &addr)` | #### 六、特殊场景注意 1. **广播地址处理** `inet_addr("255.255.255.255")`会返回`INADDR_NONE`[^4],而`inet_pton`可正确处理: ```c inet_pton(AF_INET, "255.255.255.255", &ipv4); // 返回1表示成功 ``` 2. **线程安全实践** 避免在多线程环境中使用`inet_ntoa`(与`inet_addr`配套的非线程安全函数),改用`inet_ntop`: ```c char str[INET_ADDRSTRLEN]; inet_ntop(AF_INET, &ipv4, str, INET_ADDRSTRLEN); // 线程安全的逆转换[^4] ``` --- ### 代码示例(IPv4/IPv6双协议支持) ```c #include <stdio.h> #include <arpa/inet.h> void print_ip(int af, const void *src) { char buf[INET6_ADDRSTRLEN]; const char *ret = inet_ntop(af, src, buf, sizeof(buf)); if (ret) printf("IP地址: %s\n", buf); } int main() { struct in_addr ipv4; struct in6_addr ipv6; inet_pton(AF_INET, "203.0.113.5", &ipv4); inet_pton(AF_INET6, "2001:db8:8714::3", &ipv6); print_ip(AF_INET, &ipv4); print_ip(AF_INET6, &ipv6); return 0; } ``` --- ### 相关问题 1. 如何将二进制网络地址转换回可读字符串格式?[^4] 2. `inet_ntop`函数与`inet_ntoa`在IPv6支持上有何区别?[^3] 3. 在Windows平台使用`inet_pton`需要哪些头文件库?[^1][^3] 4. 为什么`inet_addr("255.255.255.255")`会返回错误?如何正确处理广播地址?[^4] 5. 如何实现同时兼容IPv4IPv6的地址转换工具函数
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值