网络字节序与主机字节序的转换
Posted wonxxx
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了网络字节序与主机字节序的转换相关的知识,希望对你有一定的参考价值。
前言
端口号和IP地址都是以网络字节序存储的,不是主机字节序。网络字节序都是大端模式,而我们常用的机器都是小端模式。要把主机字节序和网络字节序相互对应起来,需要对这两个字节存储优先顺序进行相互转化。其实这个转换实质是:字节“搬家”。
先分析一下我们平时使用ntohl
、ntohs
、htonl
、htons
函数是怎么实现的,然后在本文最后写一个判断机器是大/小端的函数。
函数追踪
函数定义的地方:
/usr/include/netinet/in.h
# if __BYTE_ORDER == __BIG_ENDIAN
/* The host byte order is the same as network byte order,
so these functions are all just identity. */
# define ntohl(x) (x)
# define ntohs(x) (x)
# define htonl(x) (x)
# define htons(x) (x)
# else
# if __BYTE_ORDER == __LITTLE_ENDIAN
# define ntohl(x) __bswap_32 (x)
# define ntohs(x) __bswap_16 (x)
# define htonl(x) __bswap_32 (x)
# define htons(x) __bswap_16 (x)
# endif
# endif
#endif
可以看到ntohl
和htonl
使用的是同一个函数__bswap_32
,ntohs
和htons
使用的是同一个函数__bswap_16
,在这里只对__bswap_32
函数的实现进行追踪:
/usr/include/i386-linux-gnu/bits/byteswap.h
/* Swap bytes in 32 bit value. */
#define __bswap_constant_32(x) \\
((((x) & 0xff000000) >> 24) | (((x) & 0x00ff0000) >> 8) | \\
(((x) & 0x0000ff00) << 8) | (((x) & 0x000000ff) << 24))
#ifdef __GNUC__
# if __GNUC__ >= 2
/* To swap the bytes in a word the i486 processors and up provide the
`bswap' opcode. On i386 we have to use three instructions. */
# if !defined __i486__ && !defined __pentium__ && !defined __pentiumpro__ \\
&& !defined __pentium4__ && !defined __k8__ && !defined __athlon__ \\
&& !defined __k6__ && !defined __nocona__ && !defined __core2__ \\
&& !defined __geode__ && !defined __amdfam10__
# define __bswap_32(x) \\
(__extension__ \\
( register unsigned int __v, __x = (x); \\
if (__builtin_constant_p (__x)) \\
__v = __bswap_constant_32 (__x); \\
else \\
__asm__ ("rorw $8, %w0;" \\
"rorl $16, %0;" \\
"rorw $8, %w0" \\
: "=r" (__v) \\
: "0" (__x) \\
: "cc"); \\
__v; ))
# else
# define __bswap_32(x) \\
(__extension__ \\
( register unsigned int __v, __x = (x); \\
if (__builtin_constant_p (__x)) \\
__v = __bswap_constant_32 (__x); \\
else \\
__asm__ ("bswap %0" : "=r" (__v) : "0" (__x)); \\
__v; ))
# endif
# else
# define __bswap_32(x) \\
(__extension__ \\
( register unsigned int __x = (x); __bswap_constant_32 (__x); ))
# endif
#else
static __inline unsigned int
__bswap_32 (unsigned int __bsx)
return __bswap_constant_32 (__bsx);
#endif
这里有对不同的处理器有不同的处理情况,还有汇编指令,这里先不对这部分做深究,所以最终的实现为:
/* Swap bytes in 32 bit value. */
#define __bswap_constant_32(x) \\
((((x) & 0xff000000) >> 24) | (((x) & 0x00ff0000) >> 8) | \\
(((x) & 0x0000ff00) << 8) | (((x) & 0x000000ff) << 24))
虽然没有对应的64bit的转换函数接口,但在文件bits/byteswap.h中还是有实现:
#if defined __GNUC__ && __GNUC__ >= 2
/* Swap bytes in 64 bit value. */
# define __bswap_constant_64(x) \\
(__extension__ ((((x) & 0xff00000000000000ull) >> 56) \\
| (((x) & 0x00ff000000000000ull) >> 40) \\
| (((x) & 0x0000ff0000000000ull) >> 24) \\
| (((x) & 0x000000ff00000000ull) >> 8) \\
| (((x) & 0x00000000ff000000ull) << 8) \\
| (((x) & 0x0000000000ff0000ull) << 24) \\
| (((x) & 0x000000000000ff00ull) << 40) \\
| (((x) & 0x00000000000000ffull) << 56)))
# define __bswap_64(x) \\
(__extension__ \\
( union __extension__ unsigned long long int __ll; \\
unsigned long int __l[2]; __w, __r; \\
if (__builtin_constant_p (x)) \\
__r.__ll = __bswap_constant_64 (x); \\
else \\
\\
__w.__ll = (x); \\
__r.__l[0] = __bswap_32 (__w.__l[1]); \\
__r.__l[1] = __bswap_32 (__w.__l[0]); \\
\\
__r.__ll; ))
#endif
对于__BYTE_ORDER:
/* i386 is little-endian. */
#ifndef _ENDIAN_H
# error "Never use <bits/endian.h> directly; include <endian.h> instead."
#endif
#define __BYTE_ORDER __LITTLE_ENDIAN
这里大小端模式竟然是直接定义好的,还以为会用使用数据存储来判断一下呢。。。那我们自己动手来写一下大小端的判断函数:
#include <stdio.h>
#define BIG_ENDIAN 1
#define LITTLE_ENDIAN 0
#define BOOL unsigned int
/*
* 定义一个2个字节长度的数据,并赋值为1,则其16进制表示为0x0001
* 如果系统以“大端”存放数据,那么低字节存放的必定是0x00,高字节存放的必定是0x01
* 如果系统以“小端”存放数据,那么低字节存放的必定是0x01,高字节存放的必定是0x00
*/
static BOOL is_bigendian()
const short n = 1;
if (*(char *)&n)
return LITTLE_ENDIAN;
return BIG_ENDIAN;
int main()
if (BIG_ENDIAN == is_bigendian())
printf("Big Endian\\n");
else
printf("Little Endian\\n");
return 0;
结语
知其然,也要知其所以然。
以上是关于网络字节序与主机字节序的转换的主要内容,如果未能解决你的问题,请参考以下文章
手把手写C++服务器(20):网络字节序与主机字节序大端小端与共用体