记录总结自己的工作
分类: LINUX
2010-09-07 10:05:41
首先,必须说明的是网络上的数据是流式传送的,也就是经常说的stream,流式传送的特点就是一方发送的是1234四个字节,那么接收端收到的必然是1234四个字节,顺序完全相同。
其次,必须说明的是主机字节序,不同CPU保存数据的方式不同,即高位在前还是低位在前的问题的,大家可以自己google搜索一下“big little endian CPU”会发现很多人解释这个问题。对于单个字节的数据,即存储空间占用1Byte的数据,没有任何问题,但由于数据很大,通常1Byte表示不了,就必须引入多字节表示的数据,比如一个int通常为32个bit的数据,即4Byte表示一个int,那么当你定义一个int型变量x时,它的值可能是0x1234,即x=0x1234;但如果是另外一种顺序,则x=0x4321,显然这两个数相差很大。big-endian和little-endian问题简单说来就是对于同样的4个字节1234,一种顺序表示为1234,另一种顺序可能表示为4321。这就象中文表示姓名和英文表示姓名的顺序不一样,中国人姓名表示时姓在前名在后,而英文习惯里表示姓名时名在前姓在后,对于汉语的zhou lifa,英文里可能写成lifa zhou。如果双方不知道这个差别,英文习惯的人听汉语的人说人名zhou lifa里必然以为是姓lifa的叫zhou的人。
你可以用这个程序来测试你的主机字节序:
#include #include int main(void) { printf("big:%d little:%d mine:%d\n", __BIG_ENDIAN, __LITTLE_ENDIAN, __BYTE_ORDER); return 0; } |
因此,基于上述两点我们就明白了“多字节数据传送必须要关注数据顺序”。对于单字节的数据,比如char型的,没有问题,所以在前面所有例子程序中,一个程序发送“hello, Linux”,另一个程序必然同样收到这样的字符串而不会顺序乱了。但如果你在这边发过去四个字节1234表示一个int数据,对方接收到以后不知道你的机器是big-endian还是little-endian,按照它自己的理解来解释这个数字,可能这个数字就是4321了。
好在前人已经想到这个问题了,因此网络编程基础“TCP/IP协议”里已经规定所有在网络上传送的数据统一采用big-endian顺序。因此,正确的网络编程方法是这样的:
如果你要发送纯字符串给对方,那么相当容易,可以这样:
char buf[] = "This is a test string"; ...... ret = send(socket_fd, buf, strlen(buf), 0); ...... |
如果你要发送多字节数据,比如short,int,float,double,long……你必须先转换为big-endian序再发送,比如:
int age = 30; …… age = htonl(age); ret = send(socket_fd, (void *)&age, sizeof(int), 0); …… |
这里涉及一些网络字节序和主机字节序转换的函数,把主机(host)字节序转换为网络(network)字节序用到htons,htonl等函数,反之则用ntohs,ntohl等函数。man一下就知道这几个函数的用处和用法了。
对于结构数据的传送
比如有这样一个表示人员信息的结构:
struct member { char name[32]; int age; char gender; char address[128]; }; |
当你定义了一个变量struct member personalInfo并把personalInfo的各字段内容准备好了,如何把这个信息发送给对方呢?
一种方法:
发送和接收的双方都知道上述结构struct member的定义,因此,不管一个人的名字是几个字节,也不管这个人的住址信息是多少个字节,发送的一方可以这样写程序:
struct member personalInfo; personalInfo.age = htonl(personalInfo.age); |
而接收一方则可以这样写程序:
struct member personalInfo; personalInfo.age = ntohl(personalInfo.age); |
当然,这样写程序的前提仍然是双方是32位或64位系统,如果一方是32位系统,他这里表示的sizeof(int)是32bit个字节,而另一方表示的是64bit,程序工作会有问题的。
另一种方法:
发送方这样写程序:
#pragma pack(1) /* 这一行应在定义struct member时写 */ personalInfo.age = htonl(personalInfo.age); |
接收方这样写程序:
#pragma pack(1) /* 这一行应在定义struct member时写,不一定要写成1,关键是要与发送方的定义保持一致 */ personalInfo.age = ntohl(personalInfo.age); |
这种方法要注意的是双方有个一致的定义pack(1)即,按位对齐方式定义相同。在网上搜索“C pragma pack”会发现很多这样的讨论。
当然也可以试试这几段代码的结果:
#include #pragma pack(1) int main(void) { struct member { printf("size is:%d\n", sizeof(struct member)); return 0; }
char name[32];
int age;
char gender;
char address[128];
};
#include #pragma pack(2) int main(void) { struct member { printf("size is:%d\n", sizeof(struct member)); return 0; }
char name[32];
int age;
char gender;
char address[128];
};
#include #pragma pack(3) int main(void) { struct member { printf("size is:%d\n", sizeof(struct member)); return 0; }
char name[32];
int age;
char gender;
char address[128];
};
#include #pragma pack(4) int main(void) { struct member { printf("size is:%d\n", sizeof(struct member)); return 0; }
char name[32];
int age;
char gender;
char address[128];
};
#include #pragma pack(5) int main(void) { struct member { printf("size is:%d\n", sizeof(struct member)); return 0; }
char name[32];
int age;
char gender;
char address[128];
};
#include #pragma pack(6) int main(void) { struct member { printf("size is:%d\n", sizeof(struct member)); return 0; }
char name[32];
int age;
char gender;
char address[128];
};
#include #pragma pack(7) int main(void) { struct member { printf("size is:%d\n", sizeof(struct member)); return 0; }
char name[32];
int age;
char gender;
char address[128];
};
#include #pragma pack(8) int main(void) { struct member { printf("size is:%d\n", sizeof(struct member)); return 0; } |
转自:
chinaunix网友2010-09-09 10:50:35
Download More than 1000 free IT eBooks: http://free-ebooks.appspot.com