Chinaunix首页 | 论坛 | 博客
  • 博客访问: 1570951
  • 博文数量: 113
  • 博客积分: 3526
  • 博客等级: 中校
  • 技术积分: 1815
  • 用 户 组: 普通用户
  • 注册时间: 2009-09-08 09:46
个人简介

记录总结自己的工作

文章分类

全部博文(113)

文章存档

2015年(19)

2014年(10)

2013年(6)

2012年(16)

2011年(24)

2010年(21)

2009年(17)

分类: 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;
......
ret = send(socket_fd, personalInfo.name, 32, 0);
......

personalInfo.age = htonl(personalInfo.age);
ret = send(socket_fd, (void *)&personalInfo.age, sizeof(int), 0);
......
ret = send(socket_fd, &personalInfo.gender, sizeof(char), 0);
......
ret = send(socket_fd, personalInfo.address, 128, 0);
......

而接收一方则可以这样写程序:

struct member personalInfo;
......
ret = recv(socket_fd, personalInfo.name, 32, 0);
......
ret = recv(socket_fd, (void *)&personalInfo.age, sizeof(int), 0);

personalInfo.age = ntohl(personalInfo.age);
......
ret = recv(socket_fd, &personalInfo.gender, sizeof(char), 0);
......
ret = recv(socket_fd, personalInfo.address, 128, 0);
......

当然,这样写程序的前提仍然是双方是32位或64位系统,如果一方是32位系统,他这里表示的sizeof(int)是32bit个字节,而另一方表示的是64bit,程序工作会有问题的。

另一种方法
发送方这样写程序:

#pragma pack(1) /* 这一行应在定义struct member时写 */
struct member personalInfo;
......

personalInfo.age = htonl(personalInfo.age);
ret = send(socket_fd, (void *)&personalInfo, sizeof(struct member), 0);
......

接收方这样写程序:

#pragma pack(1) /* 这一行应在定义struct member时写,不一定要写成1,关键是要与发送方的定义保持一致 */
struct member personalInfo;
......
ret = recv(socket_fd, (void *)&personalInfo, sizeof(struct member), 0);

personalInfo.age = ntohl(personalInfo.age);
......

这种方法要注意的是双方有个一致的定义pack(1)即,按位对齐方式定义相同。在网上搜索“C pragma pack”会发现很多这样的讨论。


当然也可以试试这几段代码的结果:

#include

#pragma pack(1)

int main(void)

{

struct member {
        char name[32];
        int age;
        char gender;
        char address[128];
};

printf("size is:%d\n", sizeof(struct member));

return 0;

}

#include

#pragma pack(2)

int main(void)

{

struct member {
char name[32];
int age;
char gender;
char address[128];
};

printf("size is:%d\n", sizeof(struct member));

return 0;

}

#include

#pragma pack(3)

int main(void)

{

struct member {
char name[32];
int age;
char gender;
char address[128];
};

printf("size is:%d\n", sizeof(struct member));

return 0;

}

#include

#pragma pack(4)

int main(void)

{

struct member {
char name[32];
int age;
char gender;
char address[128];
};

printf("size is:%d\n", sizeof(struct member));

return 0;

}

#include

#pragma pack(5)

int main(void)

{

struct member {
char name[32];
int age;
char gender;
char address[128];
};

printf("size is:%d\n", sizeof(struct member));

return 0;

}

#include

#pragma pack(6)

int main(void)

{

struct member {
char name[32];
int age;
char gender;
char address[128];
};

printf("size is:%d\n", sizeof(struct member));

return 0;

}

#include

#pragma pack(7)

int main(void)

{

struct member {
char name[32];
int age;
char gender;
char address[128];
};

printf("size is:%d\n", sizeof(struct member));

return 0;

}

#include

#pragma pack(8)

int main(void)

{

struct member {
char name[32];
int age;
char gender;
char address[128];
};

printf("size is:%d\n", sizeof(struct member));

return 0;

}

 

转自:

阅读(2033) | 评论(1) | 转发(0) |
0

上一篇:jtable排序

下一篇:vi中的复制粘贴

给主人留下些什么吧!~~

chinaunix网友2010-09-09 10:50:35

Download More than 1000 free IT eBooks: http://free-ebooks.appspot.com