在前面文章网络编程-网段计算涉及了大小端问题,这里重新梳理下。
要考虑大小端的原因是不同的硬件结构会以不同的顺序来存储多字节整数,这里有2个关键词,一个是多字节,一个是整数。
所以如果传输的是字符串,是不用考虑字节序问题的,一个字节的数据也是不用考虑的。
从字节序的转换接口也可以看出来,如下:
#include <arpa/inet.h>
uint32_t htonl(uint32_t hostlong);
uint16_t htons(uint16_t hostshort);
uint32_t ntohl(uint32_t netlong);
uint16_t ntohs(uint16_t netshort);
再来说下大端和小端的区别,比如一个32位的整数 0x12345678,内存占用4个字节,其起始地址如果存的数据是0x12,也即起始地址存储高位数据,那就是大端,如果起始地址存的数据是0x78,也即起始地址存储低位数据,那就是小端。根据这个理解,编写检测大小端代码如下:
#include <stdio.h>
#include <stdint.h>
int main(void)
{
uint32_t num = 0x12345678;
char *p = (char *)#
if (*p == 0x12)
{
printf("big-endian.\n");
}
else
{
printf("little-endian.\n");
}
}
在网络编程中涉及大小端问题的主要就IP地址和端口号,网络传输规定应该以大端字节序的形式进行传输,也称为网络字节序。
一般而言通过调用相关网络转换接口来获取到的IP地址和端口号都是网络字节序的,可以直接赋值到对应的网络结构体中,这个通过man手册查看相应的接口说明都可以明确,其他情况则需要注意是否要进行网络字节序转换。
这里说几个常用的IPV4和IPV6宏定义的常量地址,如IPV4下面两个:
#define INADDR_LOOPBACK (u_int32_t)0x7f000001
#define INADDR_ANY (u_int32_t)0x00000000
像INADDR_ANY这样都是全0的,所以用不用字节序转换操作都一样,一般不用,但如果是使用的回环地址,则需要进行字节序转换。
IPV6如下两个地址:
#define IN6ADDR_ANY_INIT \
{{{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }}}
#define IN6ADDR_LOOPBACK_INIT \
{{{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01 }}}
const struct in6_addr in6addr_any = IN6ADDR_ANY_INIT;
const struct in6_addr in6addr_loopback = IN6ADDR_LOOPBACK_INIT;
IPV6地址是16字节的数组形式,对于in6addr_any这个都是0的也不用考虑,对于in6addr_loopback地址,首先了解IPV6的回环地址就是::1,那么从上面的数据存储形式来看,其起始地址存储的就是高位,所以本身就是网络字节序了,不需要做任何转换。
下面示例代码可以更好的说明这点:
int convert_ipv6_addr(void)
{
struct in6_addr addr;
inet_pton(AF_INET6, "fe80:f123:f456:f789:f559:fdc7:af26:a210", &addr);
printf("addr[0]: %x\n", (addr.s6_addr[0]));
printf("addr[1]: %x\n", (addr.s6_addr[1]));
printf("addr[2]: %x\n", (addr.s6_addr[2]));
printf("addr[3]: %x\n", (addr.s6_addr[3]));
printf("addr32[0]: %x\n", (((uint32_t *)addr.s6_addr)[0]));
return 0;
}
运行结果:
addr[0]: fe // 起始地址存储高位,大端存储
addr[1]: 80
addr[2]: f1
addr[3]: 23
addr32[0]: 23f180fe // 测试机主机字节序是小端,所以解析按小端计算值来进行打印输出
从运行结果可以看出来起始地址存储的就是高位数据,所以在前面网络编程-网段计算中计算IPV6地址无需再做网络字节序转换。
1、
字节序转换接口不能乱用,比如端口号进行网络字节序转换,是用htons,如果错误使用htonl,得到的结果是0。想想为什么?
2、
明确所操作的数是否已经是网络字节序了,不要重复进行字节序转换。