htonl() htons() ntohl() ntohs() inet_ntoa() inet_addr()的用法
2016-12-02 12:01
816 查看
inet_addr函数需要一个字符串作为其参数,该字符串指定了以点分十进制格式表示的IP地址(例如:192.168.0.16),而且inet_addr函数会返回一个适合分配给S_addr的u_long类型的数值。
eg:ina.sin_addr.s_addr = inet_addr("132.241.5.10");
inet_ntoa函数会完成相反的转换,它接受一个in_addr结构体类型的参数,并返回一个以点分十进制格式表示的IP地址字符串。
eg:printf("%s",inet_ntoa(ina.sin_addr));
htonl()表示将32位的主机字节顺序转化为32位的网络字节顺序(host to network long)。
htons()表示将16位的主机字节顺序转化为16位的网络字节顺序(host to network short)。
ntohl()表示将32位的网络字节顺序转化为32位的主机字节顺序(network to host long)。
ntohs()表示将16位的网络字节顺序转化为16位的主机字节顺序(network to host short)。
在详细分析各函数之前,我们先来看个相关知识点:
在C/C++写网络程序的时候,往往会遇到字节的网络顺序和主机顺序的问题,这正是因为计算机数据表示存在两种字节顺序:NBO与HBO。
网络字节顺序NBO(Network ByteOrder):网络字节顺序是TCP/IP中规定好的一种数据表示格式,它与具体的CPU类型、操作系统等无关,从而可以保证数据在不同主机之间传输时能够被正确解释。网络字节顺序采用big endian排序方式,按从高到低的顺序存储,在网络上使用统一的网络字节顺序,可以避免兼容性问题。
主机字节顺序(HBO,Host ByteOrder):不同的CPU有不同的字节序类型,这些字节序是指整数在内存中保存的顺序,这个叫做主机字节序。最常见的有两种(1)Little endian(小端模式)最符合人的思维的字节序:低字节存高地址,高字节存低地址。怎么讲是最符合人的思维的字节序,是因为从人的第一观感来说低位值最小,就应该放在内存地址小的地方,也即内存地址地位;反之,高位值就应该放在内存地址大的地方,也即内存地址高位。(2)Big endian(大端模式)最直观的字节序:低字节存低地址,高字节存高地址。为什么说直观,因为这种方式不考虑对应关系,只需要把内存地址从左到右按照由低到高的顺序写出,把值按照通常的高位到低位的顺序写出,两者对照,一个字节一个字节的填充进去。
eg:如果我们将0x1234abcd写入到以0x0000开始的内存中,则结果为:
注:x86系列CPU都是Littleendian的字节序。
不同的机器HBO不相同,与CPU设计有关,数据的顺序是由cpu决定的,而与操作系统无关。如 Intel x86结构下,short型数0x1234表示为34 12, int型数0x12345678表示为78 56 34 12。再如IBM power PC结构下,short型数0x1234表示为12 34,int型数0x12345678表示为12 34 56 78。
由于这个原因不同体系结构的机器之间无法通信,所以要转换成一种约定的数序,也就是网络字节顺序,其实就是如同power pc那样的顺序。在PC开发中有ntohl和htonl函数可以用来进行网络字节和主机字节的转换。
1. inet_addr()
函数原型:unsigned long PASCAL FAR inet_addr( const struct FAR* cp);
函数功能:将一个点间隔地址转换成一个in_addr。
一个以Internet标准“.”间隔的字符串,例如202.38.214.xx。当IP地址为255.255.255.255是被认为无效IP地址。本函数解释cp参数中的字符串,这个字符串用Internet的“.”间隔格式表示一个数字的Internet地址。返回值是一个无符号长整形数,可用作Internet地址,所有Internet地址以网络字节顺序返回(字节从左到右排列)。当
inet_addr()发生错误时返回-1。
注:当IP地址为255.255.255.255时,inet_addr()认为它是无效IP地址,而inet_ntoa()函数将其视为有效的IP。
2. inet_ntoa()
函数原型:char FAR* PASCAL FAR inet_ntoa( struct in_addr in);
函数功能:将网络地址转换成“.”点隔的字符串格式。
("ntoa"的含义是"network to ascii")
本函数将一个用in参数所表示的Internet地址结构转换成以“.”间隔的诸如“a.b.c.d”的字符串形式。请注意inet_ntoa()返回的字符串存放在WINDOWS套接口实现所分配的内存中。应用程序不应假设该内存是如何分配的。在同一个线程的下一个WINDOWS套接口调用前,数据将保证是有效。若无错误发生,inet_ntoa()返回一个字符指针;否则的话,返回NULL。其中的数据应在下一个WINDOWS套接口调用前复制出来。
注:inet_ntoa()将结构体in-addr作为一个参数,不是长整形。还需要指出,该函数返回的是一个指向一个字符的指针,它是一个由inet_ntoa()控制的静态的固定的指针,所以每次调用 inet_ntoa(),它就将覆盖上次调用时所得的IP地址。
补充:还有个函数inet_aton(),与inet_ntoa()作用相反。
3. htons()
函数原型:u_short PASCAL FAR htons( u_short hostshort);
函数功能:把unsigned short类型从主机序转换到网络序。
注:简单地说,htons()就是将一个数的高低位互换,如:12 34 --> 34 12。
eg:在Intel机器下,执行以下程序
int main()
{
<
4000
span style="font-size:18px;"> printf(“%d\n”,htons(16));
return 0;
}
得到的结果是4096,解释如下:数字16的16进制表示为0x0010,数字4096的16进制表示为0x1000。由于Intel机器是小尾端,存储数字16时的实际顺序为1000,存储4096时的实际顺序为0010。因此在发送网络包时,为了报文中数据位0010,需要经过htons进行字节转换。如果用IBM等大尾机器,则没有这种字节顺序转换,但为了程序的可移植性,也最好用这个函数。
注:数字所占位数小于或等于一个字节(8 bits)时,不要用htons转换,这是因为对于主机来说,大小尾端的最小单位为字节(byte)。
4. htonl()
函数原型:u_long PASCALFAR htonl( u_long hostlong);
函数功能:把unsigned long类型从主机序转换到网络序。
5. ntohl()
函数原型:u_long PASCAL FAR ntohl( u_long netlong);
函数功能:把unsigned long类型从网络序转换到主机序。
6. ntohs()
函数原型:u_short PASCAL FAR ntohs( u_short netshort);
函数功能:把unsigned short类型从网络序转换到主机序。
注:在使用Little endian的系统中,上面4个函数会把字节序进行转换;在使用Big endian类型的系统中,这些函数会定义成空宏。
测试代码:
include <stdio.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <string.h>
int main(int aargc, char* argv[])
{
struct in_addr addr1,addr2;
ulong l1,l2;
l1= inet_addr("192.168.0.74");
l2 = inet_addr("211.100.21.179");
memcpy(&addr1, &l1, 4);
memcpy(&addr2, &l2, 4);
printf("%s : %s\n", inet_ntoa(addr1), inet_ntoa(addr2)); //注意这一句的运行结果
printf("%s\n", inet_ntoa(addr1));
printf("%s\n", inet_ntoa(addr2));
return 0;
}
实际运行结果如下:
192.168.0.74 : 192.168.0.74 //从这里可以看出,printf里的inet_ntoa只运行了一次。
192.168.0.74
211.100.21.179
这就解释了刚刚提出来的:inet_ntoa返回一个char *,而这个char *的空间是在inet_ntoa里面静态分配的,所以inet_ntoa后面的调用会覆盖上一次的调用。第一句printf的结果只能说明在printf里面的可变参数的求值是从右到左的,仅此而已。
补充:sockaddr_in , sockaddr , in_addr区别
struct sockaddr {
unsigned short sa_family;
char sa_data[14];
};
上面是通用的socket地址,具体到Internetsocket,用下面的结构,二者可以进行类型转换
struct sockaddr_in {
short int sin_family;
unsigned short int sin_port;
struct in_addr sin_addr;
unsigned char sin_zero[8];
};
struct in_addr就是32位IP地址。
struct in_addr {
union {
struct { u_chars_b1,s_b2,s_b3,s_b4; } S_un_b;
struct { u_shorts_w1,s_w2; } S_un_w;
u_long S_addr;
} S_un;
#define s_addr S_un.S_addr
};
inet_addr()是将一个点分制的IP地址(如192.168.0.1)转换为上述结构中需要的32位IP地址(0xC0A80001)。填值的时候使用sockaddr_in结构,而作为函数(如socket,listen, bind等)的参数传入的时候转换成sockaddr结构就行了,毕竟都是16个字符长。
通常的用法是:
int sockfd;
struct sockaddr_in my_addr;
sockfd = socket(AF_INET, SOCK_STREAM, 0);
my_addr.sin_family = AF_INET;
my_addr.sin_port = htons(MYPORT);
my_addr.sin_addr.s_addr = inet_addr("192.168.0.1");
bzero(&(my_addr.sin_zero), 8);
bind(sockfd, (struct sockaddr *)&my_addr, sizeof(struct sockaddr));
可以用C++做个不太准确的假设。
sockaddr是base class;sockaddr_in等是derived class
如此一来,bind,connect,sendto,recvfrom等函数就可以使用base class来处理多种不同的derived class了。但是实际上,这是没有继承关系数据结构(C嘛),所以需要强制造型来转换数据类型。正因为如此,在sendto的时候需要给出len长度,因为不同的sockaddr_xx实现长度并不相同。
参考来源:
http://blog.csdn.net/cpp_funs/article/details/6988154 http://www.360doc.com/content/14/1113/16/16170632_424843985.shtml
eg:ina.sin_addr.s_addr = inet_addr("132.241.5.10");
inet_ntoa函数会完成相反的转换,它接受一个in_addr结构体类型的参数,并返回一个以点分十进制格式表示的IP地址字符串。
eg:printf("%s",inet_ntoa(ina.sin_addr));
htonl()表示将32位的主机字节顺序转化为32位的网络字节顺序(host to network long)。
htons()表示将16位的主机字节顺序转化为16位的网络字节顺序(host to network short)。
ntohl()表示将32位的网络字节顺序转化为32位的主机字节顺序(network to host long)。
ntohs()表示将16位的网络字节顺序转化为16位的主机字节顺序(network to host short)。
在详细分析各函数之前,我们先来看个相关知识点:
在C/C++写网络程序的时候,往往会遇到字节的网络顺序和主机顺序的问题,这正是因为计算机数据表示存在两种字节顺序:NBO与HBO。
网络字节顺序NBO(Network ByteOrder):网络字节顺序是TCP/IP中规定好的一种数据表示格式,它与具体的CPU类型、操作系统等无关,从而可以保证数据在不同主机之间传输时能够被正确解释。网络字节顺序采用big endian排序方式,按从高到低的顺序存储,在网络上使用统一的网络字节顺序,可以避免兼容性问题。
主机字节顺序(HBO,Host ByteOrder):不同的CPU有不同的字节序类型,这些字节序是指整数在内存中保存的顺序,这个叫做主机字节序。最常见的有两种(1)Little endian(小端模式)最符合人的思维的字节序:低字节存高地址,高字节存低地址。怎么讲是最符合人的思维的字节序,是因为从人的第一观感来说低位值最小,就应该放在内存地址小的地方,也即内存地址地位;反之,高位值就应该放在内存地址大的地方,也即内存地址高位。(2)Big endian(大端模式)最直观的字节序:低字节存低地址,高字节存高地址。为什么说直观,因为这种方式不考虑对应关系,只需要把内存地址从左到右按照由低到高的顺序写出,把值按照通常的高位到低位的顺序写出,两者对照,一个字节一个字节的填充进去。
eg:如果我们将0x1234abcd写入到以0x0000开始的内存中,则结果为:
内存地址 | Little endian | Big endian |
0x0000 | 0xcd | 0x12 |
0x0001 | 0xab | 0x34 |
0x0002 | 0x34 | 0xab |
0x0002 | 0x12 | 0xcd |
不同的机器HBO不相同,与CPU设计有关,数据的顺序是由cpu决定的,而与操作系统无关。如 Intel x86结构下,short型数0x1234表示为34 12, int型数0x12345678表示为78 56 34 12。再如IBM power PC结构下,short型数0x1234表示为12 34,int型数0x12345678表示为12 34 56 78。
由于这个原因不同体系结构的机器之间无法通信,所以要转换成一种约定的数序,也就是网络字节顺序,其实就是如同power pc那样的顺序。在PC开发中有ntohl和htonl函数可以用来进行网络字节和主机字节的转换。
1. inet_addr()
函数原型:unsigned long PASCAL FAR inet_addr( const struct FAR* cp);
函数功能:将一个点间隔地址转换成一个in_addr。
一个以Internet标准“.”间隔的字符串,例如202.38.214.xx。当IP地址为255.255.255.255是被认为无效IP地址。本函数解释cp参数中的字符串,这个字符串用Internet的“.”间隔格式表示一个数字的Internet地址。返回值是一个无符号长整形数,可用作Internet地址,所有Internet地址以网络字节顺序返回(字节从左到右排列)。当
inet_addr()发生错误时返回-1。
注:当IP地址为255.255.255.255时,inet_addr()认为它是无效IP地址,而inet_ntoa()函数将其视为有效的IP。
2. inet_ntoa()
函数原型:char FAR* PASCAL FAR inet_ntoa( struct in_addr in);
函数功能:将网络地址转换成“.”点隔的字符串格式。
("ntoa"的含义是"network to ascii")
本函数将一个用in参数所表示的Internet地址结构转换成以“.”间隔的诸如“a.b.c.d”的字符串形式。请注意inet_ntoa()返回的字符串存放在WINDOWS套接口实现所分配的内存中。应用程序不应假设该内存是如何分配的。在同一个线程的下一个WINDOWS套接口调用前,数据将保证是有效。若无错误发生,inet_ntoa()返回一个字符指针;否则的话,返回NULL。其中的数据应在下一个WINDOWS套接口调用前复制出来。
注:inet_ntoa()将结构体in-addr作为一个参数,不是长整形。还需要指出,该函数返回的是一个指向一个字符的指针,它是一个由inet_ntoa()控制的静态的固定的指针,所以每次调用 inet_ntoa(),它就将覆盖上次调用时所得的IP地址。
补充:还有个函数inet_aton(),与inet_ntoa()作用相反。
3. htons()
函数原型:u_short PASCAL FAR htons( u_short hostshort);
函数功能:把unsigned short类型从主机序转换到网络序。
注:简单地说,htons()就是将一个数的高低位互换,如:12 34 --> 34 12。
eg:在Intel机器下,执行以下程序
int main()
{
<
4000
span style="font-size:18px;"> printf(“%d\n”,htons(16));
return 0;
}
得到的结果是4096,解释如下:数字16的16进制表示为0x0010,数字4096的16进制表示为0x1000。由于Intel机器是小尾端,存储数字16时的实际顺序为1000,存储4096时的实际顺序为0010。因此在发送网络包时,为了报文中数据位0010,需要经过htons进行字节转换。如果用IBM等大尾机器,则没有这种字节顺序转换,但为了程序的可移植性,也最好用这个函数。
注:数字所占位数小于或等于一个字节(8 bits)时,不要用htons转换,这是因为对于主机来说,大小尾端的最小单位为字节(byte)。
4. htonl()
函数原型:u_long PASCALFAR htonl( u_long hostlong);
函数功能:把unsigned long类型从主机序转换到网络序。
5. ntohl()
函数原型:u_long PASCAL FAR ntohl( u_long netlong);
函数功能:把unsigned long类型从网络序转换到主机序。
6. ntohs()
函数原型:u_short PASCAL FAR ntohs( u_short netshort);
函数功能:把unsigned short类型从网络序转换到主机序。
注:在使用Little endian的系统中,上面4个函数会把字节序进行转换;在使用Big endian类型的系统中,这些函数会定义成空宏。
测试代码:
include <stdio.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <string.h>
int main(int aargc, char* argv[])
{
struct in_addr addr1,addr2;
ulong l1,l2;
l1= inet_addr("192.168.0.74");
l2 = inet_addr("211.100.21.179");
memcpy(&addr1, &l1, 4);
memcpy(&addr2, &l2, 4);
printf("%s : %s\n", inet_ntoa(addr1), inet_ntoa(addr2)); //注意这一句的运行结果
printf("%s\n", inet_ntoa(addr1));
printf("%s\n", inet_ntoa(addr2));
return 0;
}
实际运行结果如下:
192.168.0.74 : 192.168.0.74 //从这里可以看出,printf里的inet_ntoa只运行了一次。
192.168.0.74
211.100.21.179
这就解释了刚刚提出来的:inet_ntoa返回一个char *,而这个char *的空间是在inet_ntoa里面静态分配的,所以inet_ntoa后面的调用会覆盖上一次的调用。第一句printf的结果只能说明在printf里面的可变参数的求值是从右到左的,仅此而已。
补充:sockaddr_in , sockaddr , in_addr区别
struct sockaddr {
unsigned short sa_family;
char sa_data[14];
};
上面是通用的socket地址,具体到Internetsocket,用下面的结构,二者可以进行类型转换
struct sockaddr_in {
short int sin_family;
unsigned short int sin_port;
struct in_addr sin_addr;
unsigned char sin_zero[8];
};
struct in_addr就是32位IP地址。
struct in_addr {
union {
struct { u_chars_b1,s_b2,s_b3,s_b4; } S_un_b;
struct { u_shorts_w1,s_w2; } S_un_w;
u_long S_addr;
} S_un;
#define s_addr S_un.S_addr
};
inet_addr()是将一个点分制的IP地址(如192.168.0.1)转换为上述结构中需要的32位IP地址(0xC0A80001)。填值的时候使用sockaddr_in结构,而作为函数(如socket,listen, bind等)的参数传入的时候转换成sockaddr结构就行了,毕竟都是16个字符长。
通常的用法是:
int sockfd;
struct sockaddr_in my_addr;
sockfd = socket(AF_INET, SOCK_STREAM, 0);
my_addr.sin_family = AF_INET;
my_addr.sin_port = htons(MYPORT);
my_addr.sin_addr.s_addr = inet_addr("192.168.0.1");
bzero(&(my_addr.sin_zero), 8);
bind(sockfd, (struct sockaddr *)&my_addr, sizeof(struct sockaddr));
可以用C++做个不太准确的假设。
sockaddr是base class;sockaddr_in等是derived class
如此一来,bind,connect,sendto,recvfrom等函数就可以使用base class来处理多种不同的derived class了。但是实际上,这是没有继承关系数据结构(C嘛),所以需要强制造型来转换数据类型。正因为如此,在sendto的时候需要给出len长度,因为不同的sockaddr_xx实现长度并不相同。
参考来源:
http://blog.csdn.net/cpp_funs/article/details/6988154 http://www.360doc.com/content/14/1113/16/16170632_424843985.shtml
相关文章推荐
- htons(), ntohs(),htonl(),ntohl(), inet_addr() ,inet_ntoa() || bzero()
- Socket中常见的几个转换函数(htonl,htons,ntohl,ntohs,inet_addr,inet_ntoa)
- ntohs, ntohl, htons,htonl, inet_addr,inet_ntoa区别
- Socket中常见的几个转换函数(htonl,htons,ntohl,ntohs,inet_addr,inet_ntoa)
- 网络字节序与主机字节序的转换(htonl,htons,inet_addr,ntohl,ntohs,inet_ntoa)
- inet_ntop(), inet_pton() inet_ntoa(), inet_aton(), inet_addr, htons(), htonl(), ntohs(), ntohl() struct hostent ,struct sockaddr_in
- htons(), ntohs(),htonl(),ntohl(), inet_addr() ,inet_ntoa() || bzero()
- htonl() htons()及inet_ntoa() inet_addr()的用法
- htonl() htons()及inet_ntoa() inet_addr()的用法
- htonl() htons()及inet_ntoa() inet_addr()的用法
- htonl() htons()及inet_ntoa() inet_addr()的用法
- htonl() htons()及inet_ntoa() inet_addr()的用法
- htonl() htons()及inet_ntoa() inet_addr()的用法
- htonl()_htons()及inet_ntoa()_inet_addr()的用法
- htonl() htons()及inet_ntoa() inet_addr()的用法
- htonl() htons()及inet_ntoa() inet_addr()的用法 .
- htonl() htons()及inet_ntoa() inet_addr()的用法
- htonl() htons()及inet_ntoa() inet_addr()的用法
- htonl() htons()及inet_ntoa() inet_addr()的用法
- htonl() htons()及inet_ntoa() inet_addr()的用法