Linux环境下点分十进制和整型IP间的相互转换

本文介绍Linux环境下使用Unix网络编程实现点分十进制和整型ip地址间的相互转换,以及那些应该规避的问题。
先从示例代码讲起:

//输入点分十进制的IP,将其转换成整型后,再反向转换进行验证
#include 
#include 
#include 
#include 
#include 

using namespace std;

unsigned int IPtoInt(char *str_ip)
{
    in_addr addr;
    unsigned int int_ip;
    if(inet_aton(str_ip,&addr))
    {
        int_ip = ntohl(addr.s_addr);
    }
    return int_ip;
}

string IpToDot(unsigned int nIp){
    in_addr addr;
    addr.s_addr = htonl(nIp); 
    string strip = inet_ntoa(addr);
    if(!strip.empty()){
        return strip;
    }
    return NULL;
}

int main(int argc, char** argv)
{
    for(int i = 1 ; i < argc ; ++i){
        unsigned int nIp = IPtoInt(argv[i]);
        cout<cout<

代码详解:
in_addr_t为32位无符号整型,通常用于以网络字节序(大端字节序)表示ip地址,其定义如下:

#include 
/* Internet address.  */
typedef uint32_t in_addr_t;
struct in_addr
{
    in_addr_t s_addr;
}; 

字节序
对于应用程序,有4个用来处理主机字节序和网络字节序之间的相互转换。所谓的主机字节序是指当前主机内部的数据组织方式,包括大端字节序和小端字节序两种方式。网络字节序是一种大端字节序,它是TCP/IP中定义好的一种数据表示形式,与具体主机的实现形式无关。由于通信双方的主机可能是以不同的字节序表示IP,故在网络传输时需要统一转成网络字节序以保持一致性。

#include 
uint32_t ntohl (uint32_t __netlong);  //返回:以主机字节序表示的32位整数
uint16_t ntohs (uint16_t __netshort); //返回:以主机字节序表示的16位整数
uint32_t htonl (uint32_t __hostlong); //返回:以网络字节序表示的32位整数
uint16_t htons (uint16_t __hostshort);//返回:以网络字节序表示的16位整数
//h表示主机字节序,n表示网络字节序。l表示长整型,s表示端整型。

点分十进制和整型IP的转换
以下三个函数提供了在点分十进制与它的32位网络字节序二进制值间转换IPv4地址。

#include 
/* Convert Internet host address from numbers-and-dots notation in CP
  into binary data and store the result in the structure INP.  
  */
extern int inet_aton (__const char *__cp, struct in_addr *__inp);

/* Convert Internet number in IN to ASCII representation.  The return value
   is a pointer to an internal array containing the string.  
   */
extern char *inet_ntoa (struct in_addr __in) ;

/* Convert Internet host address from numbers-and-dots notation in CP
  into binary data in network byte order.  */
extern in_addr_t inet_addr (__const char *__cp);

inet_aton将__cp所指的C字符串转换成32位的网络字节序二进制值,并通过指针__inp存储。转换成功则返回1,否则返回0。
inet_ntoa将一个32位的网络字节序二进制IPv4地址转换成相应的点分十进制。函数返回值所指的字符串会驻留在静态内存中,因此是不可重入的。
inet_addr是不建议使用的,它的功能与inet_aton类似,但当转换出错时,返回一个常值INADDR_NONE(一般为32位均为1的值)。这就意味着点分十进制的255.255.255.255不能由此函数处理。

inet_ntoa的非线程安全
前面指出该函数的转换结果会驻留在静态内存中,是不可重入的。下面的代码予以解释:

in_addr addr1;
in_addr addr2;
//将两个不同的整型IP转换后观察结果
addr1.s_addr = htonl(252615115); 
addr2.s_addr = htonl(362615115); 

char *p1 = inet_ntoa(addr1);
char *p2 = inet_ntoa(addr2);

cout<cout<//输出
21.157.17.75
21.157.17.75

推荐使用inet_pton和inet_ntop
inet_aton和inet_ntoa只能转换IPv4,且inet_ntoa是非线程安全的,下面介绍的inet_pton和inet_ntop则可以同时支持IPv4和IPv6的转换,并且他们都是线程安全的。

#include 
 /* Convert from presentation format of an Internet number in buffer
     starting at CP to the binary network format and store result for
     interface type AF in buffer starting at BUF.  */
//返回值说明:1:成功,0:输入不是有效的表达式,-1:出错
int inet_pton (int __af, __const char *__restrict __cp,void *__restrict __buf);

 /* Convert a Internet address in binary network format for interface
    type AF in buffer starting at CP to presentation form and place
    result in buffer of length LEN astarting at BUF.  */
//返回值说明:NULL:出错,否则返回指向结果的指针
const char *inet_ntop (int __af, __const void *__restrict __cp,char *__restrict __buf, socklen_t __len);

参数说明:
__af:表示地址蔟,AF_INET表示IPv4,AF_INET6表示IPv6
inet_pton的将输入点分十进制参数__cp转换后,保存在__buf
inet_ntop将输入的整型转换后保存在__buf,__len是转换结果目标的大小,用于防止溢出。

改写后的函数如下:

#include 

unsigned int IPtoInt(char *str_ip)
{
    in_addr addr;
    unsigned int int_ip;
    if(inet_pton(AF_INET,str_ip,&addr))
    {
        int_ip = ntohl(addr.s_addr);
    }
    return int_ip;
}

string IpToDot(unsigned int nIp){
    in_addr addr;
    addr.s_addr = htonl(nIp); 
    char strip[INET_ADDRSTRLEN];
    const char *ptr = inet_ntop(AF_INET,&addr,strip,sizeof(strip));
    if(NULL != ptr){
        return strip;
    }
    return NULL;
}

你可能感兴趣的:(C++,网络,C)