服务器和客户端信息的获取
一、字节序
字节序是由于不同的主处理器和操作系统,对于一个字节的变量在内存中的存放顺序不同而产生。例:2个字节的short int
和4个字节的int
类型变量都有字节序问题。字节序通常有大端字节序和小端字节序两种。
1.大端字节序和小端字节序
字节序是由于CPU和OS对多字节变量的内存存储顺序不同而产生的。
1.字节序介绍
大于一个字节的变量类型的表示方法有以下两种:
- 小端字节序:在表示变量的内存地址的起始地址存放低字节,高字节顺序存放。
- 大端字节序:在表示变量的内存地址的起始地址存放高字节,低字节顺序存放。
例:变量的值0xabcd
,在大端字节序和小端字节序的系统中二者的存放顺序是不同的,
小端字节序系统的存放如下所示,假设存放值0xabcd
的内存地址的起始地址为0,则0xab在地址15-8的地址上,而0xcd在地址7-0的位置上。
大端字节序系统中的存放顺序如下图,假设存放值0xabcd
的内存地址起始地址为0,则0xab在地址7-0的地址上,而0xcd在地址15-8的位置上。
2.字节序的例子
下面用于上面两图所示变量在内存表示方法,确定系统中的字节序为大端字节序还是小端字节序。
(1)字节序结构。程序首先
#include<stdio.h>
typedef union{
unsigned short int value;
unsigned char byte[2];
}to;//字节序结构,建立联合类型to,用于测试字节序,成员value是short类型变量,可以通过成员byte来访问value变量的高字节和低字节。
int main(int argc,char *argv[])
{
//声明变量,将值0xabcd赋给成员变量value。由于在类型to中,value和byte成员共享一块内存,所以可以通过byte的不同成员来访问value的高字节和低字节。
to typeorder;
typeorder.value = 0xabcd;
//小端字节序判断。小端字节序的检查通过判断typeorder变量的byte成员高字节和低字节的值来进行:低字节值为0xcd,高字节的值为0xab。
if(typeorder.byte[0] == 0xcd && typeorder.byte[1]==0xab){
printf("Low endian byte order"
"byte[0]:0x%x,byte[1]:0x%x\n",
typeorder.byte[0],
typeorder.byte[1]);
}
//大端字节序判断。大端字节序的检查同样通过判断typeorder变量的byte成员高字节和低字节的值来进行:低字节的值为0xab,高字节的值为0xcd。
if(typeorder.byte[0] == 0xab && typeorder.byte[1]==0xcd){
printf("High endian byte oreder"
"byte[0]:0x%x,byte[1]:0X%x\n",
typeorder.byte[0],
typeorder.byte[1]);
}
return 0;
}
编译运行:
2.字节序转换函数
主机字节序不能统一,但必须有个统一表示方法。网络字节序是指多字节变量在网络上传输时的表示方法,网络字节序采用高端字节序的表示方法,这样小端字节序的系统通过网络传输变量的时候,需要进行字节序的转换,大端字节序的变量则不需要进行转换。
1.字节序转换函数介绍
Linux操作系统提供了与平台无关的函数进行字节序的转换:
#include<arpa/inet.h>
uint32_t htonl(uint_32_t hostlong);//主机字节序到网络字节序的长整型转换
uint16_t htons(uint16_t hostshort);//主机字节序到网络字节序的短整型转换
uint32_t ntohl(uint32_t netlong);//网络字节序到主机字节序的短长型转换
uint16_t ntohs(uint16_t netshort);//网络字节序到主机字节序的短短型转换
- 函数传入的变量为需要转换的变量,返回值为转换后的数值。
- 函数命名规则为**“字节序” “to” “字节序” “变量类型”。**上述函数,
h
:主机字节序;n
:network(网络字节序);l
:long型变量;s
:short型变量。函数htonl()的含义为"主机字节序"转换为"网络字节序",操作的变量为"long型变量"。其他函数类似。 - 两个对short类型进行转换的函数为
htons()
和ntohs()
,两个对long类型变量进行转换的函数为htonl()
和ntohl()
。
注:用户在进行程序设计的时候,需要调用字节序转换函数将主机的字节序转换为网络字节序,至于是否交换字节的顺序,则由字节序转换函数的实现来保证。
也就是说对于用户来说这种转换是透明的,只要将需要在网络上传输的变量调用一遍此类的转换函数进行一次转换即可,不用考虑目标系统的主机字节序方式。
2.字节序转换的方法
-
以上函数的作用是对字节进行交换,在大端字节序系统上,上述的字节序转换函数的实际实现可能是空的,即不进行字节序的转换;
-
对于小端字节序系统,需要将字节在变量中的顺序进行交换,例如16b的变量交换高低两个字节的位置,32b的变量将0、1、2、 3位置的字节按照0和2、1和3字节进行交换的方法。
在一个小端主机字节序系统上,进行16位数值交换的方法如下图所示:
进行32位变量的交换方法如下图所示:
-
16b的变量的字节序交换方法,在小端字节序主机系统中,进行转换时,将高地址的字节和低地址的字节进行交换;
-
32b的变量进行字节序交换的方法,在小端字节序主机系统中,进行字节序交换时,第0个字节的值与第3个字节的值进行交换,第1个字节的值与第2个字节的值进行交换。
-
字节序交换的作用是生成一个网络字节序的变量,其字节的顺序与主机类型和操作系统无关。
-
进行网络字节序转换的时候,只要转换一次就可以了,不要进行多次的转换。
-
如果进行多次字节序的转换,最后生成的网络字节序的值可能是错误的。
例如,对于主机为小端字节序的系统,进行两次字节序转换的过程如下图所示,经过两次转换,最终的值与最初的主机字节序相同。
3.字节操控函数
#include<string.s>
void bzero(void *dest,size_t nbytes);//将目标字符串指定数目的字节置为0
void bcopy(const void *src,void *dest,size_t nbytes);//将指定数目的字节从源字节串移到目标字符串。
int bcmp(const void *ptrl,const void *ptr2,size_t nbytes);//比较两个任意的字节串,相同返回值为0,否则非0
#include<string.s>
void *memset(void *dest,int c,size_t len);//把目标字符串数目置为c
void *memcpy(void *dest,const void *src,size_t nbytes);//将指定数目的字节从源字节串移到目标字符串。两个指针参数的顺序是相反的
int memcmp(const void *ptrl,const void *ptr2,size_t nbytes);//比较任意的两个字符串,相同为0,否则非0
4.一个字节序转换的例子
下面对16位数值和32位数值进行字节序转换,每种类型的数值进行两次转换,最后打印结果。
#include<stdio.h>
#include<arpa/inet.h>
#define BITS16 16
#define BITS32 32
//联合类型的变量类型,用于测试字节序
//成员value的高低端字节可以由成员type按字节访问
//16位字节序转换的结构
typedef union{
unsigned short int value;//16位short类型变量value
unsigned char byte[2];//char类型数组,共16位
}to16;
//32位字节序转换的结构
typedef union{
unsigned long int value;//32位unsigned long类型变量
unsigned char byte[4];//char类型数组,共32位
}to32;
//此函数用于打印变量值,打印的方式从变量存储空间的第一个字节开始,按照字节进行打印。
void showvalue(unsigned char *begin,int flag)//变量的地址指针begin,字长标志flag。
{
int num=0,i=0;
if(flag == BITS32){
num=2;
}else if(flag==BITS32){
num=4;
}
for(i=0;i<num;i++)
{
printf("%x",*(begin+i));
}
printf("\n");
}
int main()
{
//v16_orig是16位变量的原始值,v16_turn1是变量进行第一次字节序转换后的结果,
//v16_turn2是变量进行第二次字节序转换后的结果,
to16 v16_orig,v16_turn1,v16_turn2;
to32 v32_orig,v32_turn1,v32_turn2;
v16_orig.value = 0xabcd;//赋值
v16_turn1.value = htons(v16_orig.value);//第一次
v16_turn2.value = htons(v16_turn1.value);//第二次
v32_orig.value = 0x12345678;//赋值
v32_turn1.value = htonl(v32_orig.value);//第一次
v32_turn2.value = htonl(v32_turn1.value);//第二次
printf("16 host to network byte order change:\n");
printf("\torig:\t");showvalue(v16_orig.byte,BITS16);
printf("\t1 times:");showvalue(v16_turn1.byte,BITS16);
printf("\t2 times:")