为什么inet_ntoa会返回错误的IP地址?

目录

1、调用inet_addr和inet_ntoa实现整型IP与点式字符串之间的转换

1.1、调用inet_addr将点式字符串IP转换成整型IP

1.2、调用inet_ntoa将整型IP转换成点式字符串IP

2、调用inet_ntoa返回错误点式字符串IP的原因分析

3、解决多线程调用inet_ntoa返回错误点式字符串IP的办法

3.1、直接编写将32位整型转成点式字符串的代码

3.2、使用线程安全的inet_ntop函数来代替inet_ntoa函数


C++软件异常排查从入门到精通系列教程(专栏文章列表,欢迎订阅,持续更新...)icon-default.png?t=O83Ahttps://blog.csdn.net/chenlycly/article/details/125529931C/C++实战专栏(专栏文章已更新400多篇,持续更新中...)icon-default.png?t=O83Ahttps://blog.csdn.net/chenlycly/article/details/140824370C++ 软件开发从入门到精通(专栏文章,持续更新中...)icon-default.png?t=O83Ahttps://blog.csdn.net/chenlycly/category_12695902.htmlVC++常用功能开发汇总(专栏文章列表,欢迎订阅,持续更新...)icon-default.png?t=O83Ahttps://blog.csdn.net/chenlycly/article/details/124272585C++软件分析工具从入门到精通案例集锦(专栏文章,持续更新中...)icon-default.png?t=O83Ahttps://blog.csdn.net/chenlycly/article/details/131405795开源组件及数据库技术(专栏文章,持续更新中...)icon-default.png?t=O83Ahttps://blog.csdn.net/chenlycly/category_12458859.html网络编程与网络问题分享(专栏文章,持续更新中...)icon-default.png?t=O83Ahttps://blog.csdn.net/chenlycly/category_2276111.html       程序中有时要用整型IP地址,有时要用点式字符串IP(比如软件UI界面展示时会用点式字符串IP),有时需要做整型IP与点式字符串IP转换。本文就来讲讲整型IP与点式字符串IP之间相互转换的内容,重点讲一下调用inet_ntoa函数时为什么会返回错误的IP地址。

1、调用inet_addr和inet_ntoa实现整型IP与点式字符串之间的转换

       系统提供了inet_addr和inet_ntoa两个API函数以实现整型IP与点式字符串之间的转换。

1.1、调用inet_addr将点式字符串IP转换成整型IP

       inet_addr函数的声明如下:

给该函数传入点式字符串IP,返回网络序的整型IP,示例代码如下:

DWORD dwIp = inet_addr("172.16.80.88");

1.2、调用inet_ntoa将整型IP转换成点式字符串IP

       inet_ntoa函数的声明如下:

给该函数传入网络序的整型IP(函数参数是in_addr结构体,要将网络序的整型IP设置到in_addr结构体对应的字段中),返回点式字符串IP。示例代码如下:

char* pIpStr = NULL;
in_addr addr;
addr.S_un.S_addr = dwIp; // 网络序的整型IP
pIpStr = inet_ntoa(addr);

2、调用inet_ntoa返回错误点式字符串IP的原因分析

       调用inet_ntoa将整型IP转换成点式字符串IP示例代码如下:

char* pIpStr = NULL;
in_addr addr;
addr.S_un.S_addr = dwIp; // 网络序的整型IP
pIpStr = inet_ntoa(addr);

如果调用inet_ntoa函数返回错误的点式字符串IP,可能是以下原因导致的:

1)参数错误:inet_ntoa函数的参数in_addr结构体,该结构体中某个字段就是32位整型的IPv4地址。如果传递给inet_ntoa函数的参数不是一个有效的in_addr结构体,函数将无法正确转换成点式IP字符串。确保传递给inet_ntoa函数的参数是正确的。
2)字节序问题:传入inet_ntoa函数的in_addr结构体参数中的是网络字节序(大端字节序)的整型IP地址。如果传入的是主机字节序(小端字节序)整型IP,函数将返回错误的点式IP字符串。在使用inet_ntoa函数之前,确保传入的是网络字节序的整型IP。
3)线程安全性问题(多线程同时调用inet_ntoa问题)inet_ntoa函数是一个不可重入函数,它使用一个静态缓冲区来存储转换后的IP地址字符串,即所有调用inet_ntoa接口的地方都使用同一块缓冲区。所以,如果多个线程同时调用inet_ntoa函数,可能会导致返回错误的IP地址。为了解决这个问题,可以使用线程安全的inet_ntop函数来代替inet_ntoa函数。

       前两个原因比较简单,第三个多线程调用的场景,则具有一定的隐蔽性,出问题时较难发现。


       在这里,给大家重点推荐一下我的几个热门畅销专栏,欢迎订阅:(博客主页还有其他专栏,可以去查看)

专栏1:该精品技术专栏的订阅量已达到520多个,专栏中包含大量项目实战分析案例,有很强的实战参考价值,广受好评!专栏文章持续更新中,预计更新到200篇以上!欢迎订阅!)

C++软件调试与异常排查从入门到精通系列文章汇总icon-default.png?t=O83Ahttps://blog.csdn.net/chenlycly/article/details/125529931

本专栏根据多年C++软件异常排查的项目实践,系统地总结了引发C++软件异常的常见原因以及排查C++软件异常的常用思路与方法,详细讲述了C++软件的调试方法与手段,以图文并茂的方式给出具体的项目问题实战分析实例(很有实战参考价值),带领大家逐步掌握C++软件调试与异常排查的相关技术,适合基础进阶和想做技术提升的相关C++开发人员!

考察一个开发人员的水平,一是看其编码及设计能力,二是要看其软件调试能力!所以软件调试能力(排查软件异常的能力)很重要,必须重视起来!能解决一般人解决不了的问题,既能提升个人能力及价值,也能体现对团队及公司的贡献!

专栏中的文章都是通过项目实战总结出来的,包含大量项目问题实战分析案例,有很强的实战参考价值!专栏文章还在持续更新中,预计文章篇数能更新到200篇以上!

专栏2:(本专栏涵盖了C++多方面的内容,是当前重点打造的专栏,订阅量已达160多个,专栏文章已经更新到400多篇,持续更新中...)

C/C++实战进阶(专栏文章,持续更新中...)icon-default.png?t=O83Ahttps://blog.csdn.net/chenlycly/category_11931267.html

以多年的开发实战为基础,总结并讲解一些的C/C++基础与项目实战进阶内容,以图文并茂的方式对相关知识点进行详细地展开与阐述!专栏涉及了C/C++领域多个方面的内容,包括C++基础及编程要点(模版泛型编程、STL容器及算法函数的使用等)、数据结构与算法、C++11及以上新特性(不仅看开源代码会用到,日常编码中也会用到部分新特性,面试时也会涉及到)、常用C++开源库的介绍与使用、代码分享(调用系统API、使用开源库)、常用编程技术(动态库、多线程、多进程、数据库及网络编程等)、软件UI编程(Win32/duilib/QT/MFC)、C++软件调试技术(排查软件异常的手段与方法、分析C++软件异常的基础知识、常用软件分析工具使用、实战问题分析案例等)、设计模式、网络基础知识与网络问题分析进阶内容等。

专栏3:  

C++常用软件分析工具从入门到精通案例集锦汇总(专栏文章,持续更新中...)icon-default.png?t=O83Ahttps://blog.csdn.net/chenlycly/article/details/131405795

常用的C++软件辅助分析工具有SPY++、PE工具、Dependency Walker、GDIView、Process Explorer、Process Monitor、API Monitor、Clumsy、Windbg、IDA Pro等,本专栏详细介绍如何使用这些工具去巧妙地分析和解决日常工作中遇到的问题,很有实战参考价值!

专栏4:   

VC++常用功能开发汇总(专栏文章,持续更新中...)icon-default.png?t=O83Ahttps://blog.csdn.net/chenlycly/article/details/124272585

将10多年C++开发实践中常用的功能,以高质量的代码展现出来。这些常用的高质量规范代码,可以直接拿到项目中使用,能有效地解决软件开发过程中遇到的问题。

专栏5: 

C++ 软件开发从入门到精通(专栏文章,持续更新中...)icon-default.png?t=O83Ahttps://blog.csdn.net/chenlycly/category_12695902.html

根据多年C++软件开发实践,详细地总结了C/C++软件开发相关技术实现细节,分享了大量的实战案例,很有实战参考价值。


3、解决多线程调用inet_ntoa返回错误点式字符串IP的办法

       上面讲到了,inet_ntoa函数是一个不可重入函数,它使用一个静态缓冲区来存储转换后的IP地址字符串,即所有调用inet_ntoa接口的地方都使用同一块缓冲区。所以,如果多个线程同时调用inet_ntoa函数,可能会导致返回错误的IP地址。

         为了解决这个多线程调用inet_ntoa可能返回错误的点式字符串IP的问题,主要有以下两个方法。

3.1、直接编写将32位整型转成点式字符串的代码

       直接根据32整型IP与点式字符串构成的对应关系,可以编写如下的将32位整型转成点式字符串的代码,如下所示:

string GetIpStr(DWORD dwIPAddr)
{
    string strIP;

    char szBuf[64] = { 0 };
    unsigned short wHigh = HIWORD( dwIPAddr );
    unsigned short wLow = LOWORD( dwIPAddr );
    sprintf( szBuf, "%d.%d.%d.%d", HIBYTE( wHigh ), LOBYTE( wHigh ),
HIBYTE( wLow ), LOBYTE( wLow ) );

    strIP = szBuf;
    return strIP;
}

        注意,这个地方不能直接返回szBuf,因为szBuf是个局部变量,函数结束了,其内存就释放了。返回一个string类对象,在程序运行时会返回一个string临时对象供外部使用。

3.2、使用线程安全的inet_ntop函数来代替inet_ntoa函数

        inet_ntop函数的声明如下:

inet_ntop函数将 IPv4 或 IPv6 Internet 网络地址转换为 Internet 标准格式的字符串。关于该函数的详细说明,可以参见微软MSDN官方的说明:https://learn.microsoft.com/zh-cn/windows/win32/api/ws2tcpip/nf-ws2tcpip-inet_ntopicon-default.png?t=O83Ahttps://learn.microsoft.com/zh-cn/windows/win32/api/ws2tcpip/nf-ws2tcpip-inet_ntop       调用inet_ntop函数的示例代码如下所示:

// 1、IPv4 示例  
struct in_addr ipv4Addr;  
memset(&ipv4Addr, 0, sizeof(ipv4Addr));  
// 设置IPv4地址为192.168.1.1  
ipv4Addr.s_addr = inet_addr("192.168.1.1");  
  
char ipv4Str[INET_ADDRSTRLEN];  
inet_ntop(AF_INET, &ipv4Addr, ipv4Str, INET_ADDRSTRLEN);  
std::cout << "IPv4 Address: " << ipv4Str << std::endl;  
  
// 2、IPv6 示例  
struct in6_addr ipv6Addr;  
memset(&ipv6Addr, 0, sizeof(ipv6Addr));  
// 设置IPv6地址为2001:0db8:85a3:0000:0000:8a2e:0370:7334  
inet_pton(AF_INET6, "2001:0db8:85a3:0000:0000:8a2e:0370:7334", &ipv6Addr);  
  
char ipv6Str[INET6_ADDRSTRLEN];  
inet_ntop(AF_INET6, &ipv6Addr, ipv6Str, INET6_ADDRSTRLEN);  
std::cout << "IPv6 Address: " << ipv6Str << std::endl;  

在使用inet_ntop接口时需要注意一下:

该接口在Windows Vista及以上系统才支持,Windows XP系统中是没有这个接口的。如果程序中调用了inet_ntop接口,将程序拿到XP系统中运行,启动会报如下的错误:

系统中找不到inet_ntop接口,因为XP系统是不支持这个接口的。可以判断当前系统的版本,在Vista及以上系统中再去调用inet_ntop接口,判断系统版本的代码如下:

#include <lm.h>
#pragma comment(lib, "netapi32.lib")

BOOL IsCurOSVistaOrAbove()
{
    BOOL bOSVistaOrAbove = FALSE;
    DWORD dwMajorVer = 0;
    DWORD dwMinorVer = 0;

    // 调用NetWkstaGetInfo获取系统版本
    DWORD dwLevel = 100;
    LPWKSTA_INFO_100 lpWkStaInfo100 = NULL;
    NET_API_STATUS statusRet = NetWkstaGetInfo( NULL, dwLevel, (LPBYTE*)&lpWkStaInfo100 );
    if ( statusRet == NERR_Success )
    {
        bGetSuccess = TRUE;

        dwMajorVer = lpWkStaInfo100->wki100_ver_major;
        dwMinorVer = lpWkStaInfo100->wki100_ver_minor;

        // Free the allocated memory
        if ( lpWkStaInfo100 != NULL )
        {
            NetApiBufferFree( lpWkStaInfo100 );
        }
    }
    else
    {
        return FALSE;
    }

                // 系统版本号是6.0及以上,则是Vista及以上系统
    if ( ( dwMajorVer == 6 && dwMinorVer >= 0 ) || dwMajorVer > 6 )
    {
        bOSVistaOrAbove = TRUE;
    }

    return bOSVistaOrAbove;
}

       调用NetWkstaGetInfo系统API接口时:

需要包含lm.h头文件,同时要引入netapi32.lib库。获取系统版本号老的API接口GetVersionEx已经被微软废弃,在获取win8和win10等高版本系统时会有问题,所以此处使用NetWkstaGetInfo系统API接口。关于Windows系统版本的详细说明,可以查看我之前写的文章:

C++ 获取Widnows操作系统版本(附源码)icon-default.png?t=O83Ahttps://blog.csdn.net/chenlycly/article/details/139593706

评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

dvlinker

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值