一、大小端复习
在了解网络字节序函数前,让我们先复习一下计算机的大小端问题。
小端存储:
数据低位存储在地址低位,数据高位存储在地址高位。
大端存储:
数据低位存储在地址高位,数据高位存储在地址低位。
下面给出两种判断自己主机字节序的方法,仅供参考。
//方法1:使用指针
#include <stdio.h>
int main(int argc, const char* argv[])
{
int a = 0x12345678;
char* p = (char*)&a;
if(&p == 0x78)
{
printf("小端\n");
}
else if(&p == 0x12)
{
printf("大端\n");
}
return 0;
}
//方法2:使用联合体
#include <stdio.h>
union test
{
int a;
char b;
};
int main(int argc, const char* argv[])
{
union test t;
t.a = 0x12345678;
if(t.b == 0x78)
{
printf("小端\n");
}
else if(t.b == 0x12)
{
printf("大端\n");
}
}
二、网络字节序
如果多台主机在通信时,字节序不一样,则实际接收到的数据可发送的数据就有可能不同。所以为了保证不同字节序的主机都能收到相同的数据,就发明了网络字节序(网络字节序规定为大端存储)。
也就是说:
当发送方发送数据时,需要将数据转换成网络字节序再发送。
接收方接收数据时,也需要将网络字节序转换成自己的主机字节序在再处理。
思考:什么时候需要转换字节序,需要转换字节序的数据有什么性质?
- 如果数据是一个字节的,就不需要转换字节序了。
- 如果能明确发送端和接收端的字节序是一样的,也可以不考虑字节序
- 如果将超过1字节的数据作为一个整体发送时,就需要考虑字节序的问题,例:int。(char s[11] = "hello world"这种也是不需要考虑的)。
那么如果没有给出字节序转换的函数,我们又该如何实现呢?
int a = 0x12345678;
if(小端)
{
char *p = &a;
char *q = p+3;
交换*p 和 *q
然后 p++ q--
交换*p 和 *q
}
else if(大端)
{
不用动
}
三、字节序函数
千呼万唤终于到了我们今天要说的重点了!
字节序转换涉及4个函数:htons(), ntohs(), htonl(), ntohl()。
h ==> host
n ==> net
s ==> short
l ==> long
通常16bit的IP端口号用前两个函数来处理,而IP地址用后两个函数来处理。
函数格式
头文件 | #include <arpa/inet.h> |
---|---|
函数原型 | uint16_t htons(uint16_t hostshort); uint16_t ntohs(uint16_t netshort); uint32_t htonl(uint32_t hostlong); uint32_t ntohl(uint32_t netlong); |
函数参数 | hostshort :主机字节序的16bit数据 netshort:网络字节序的16bit数据 hostlong:主机字节序的32bit数据 netlong:网络字节序的32bit数据 |
函数返回值 | 成功:返回转换字节序后的数值 出错:-1 |
函数实例
#include <stdio.h>
#include <arpa/inet.h>
int main(int argc, const char *argv[])
{
unsigned int a = 0x12345678;
unsigned int b = htonl(a);
printf("%#x --> %#x\n", a, b);
return 0;
}