linux网络编程相互收发数据,Linux网络编程多字节数据的收发(上)

首先,必须说明的是网络上的数据是流式传送的,也就是经常说的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;

}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Linux网络编程中,使用多线程进行收发操作可以提高网络应用程序的并发性能。下面是一个简单的示例代码,说明如何使用多线程进行收发操作。 ```c #include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h> #include <pthread.h> #include <sys/socket.h> #include <arpa/inet.h> #define MAX_CLIENTS 10 void *handle_client(void *arg) { int client_fd = *(int *)arg; char buffer[1024]; memset(buffer, 0, sizeof(buffer)); // 接收客户端数据 ssize_t recv_len = recv(client_fd, buffer, sizeof(buffer), 0); if (recv_len > 0) { printf("Received message from client: %s\n", buffer); } // 发送响应给客户端 char response[] = "Hello from server!"; ssize_t send_len = send(client_fd, response, sizeof(response), 0); if (send_len == -1) { perror("send"); } close(client_fd); pthread_exit(NULL); } int main() { int server_fd; struct sockaddr_in server_addr; struct sockaddr_in client_addr; socklen_t client_addr_len = sizeof(client_addr); pthread_t threads[MAX_CLIENTS]; // 创建套接字 server_fd = socket(AF_INET, SOCK_STREAM, 0); if (server_fd == -1) { perror("socket"); exit(1); } // 设置服务器地址 server_addr.sin_family = AF_INET; server_addr.sin_addr.s_addr = INADDR_ANY; server_addr.sin_port = htons(8080); // 绑定套接字到服务器地址 if (bind(server_fd, (struct sockaddr *)&server_addr, sizeof(server_addr)) == -1) { perror("bind"); exit(1); } // 监听连接 if (listen(server_fd, MAX_CLIENTS) == -1) { perror("listen"); exit(1); } printf("Server started. Listening on port 8080...\n"); // 接受并处理客户端请求 int i = 0; while (1) { int client_fd = accept(server_fd, (struct sockaddr *)&client_addr, &client_addr_len); if (client_fd == -1) { perror("accept"); continue; } // 创建线程处理客户端请求 if (pthread_create(&threads[i], NULL, handle_client, &client_fd) != 0) { perror("pthread_create"); continue; } // 分离线程 pthread_detach(threads[i]); i++; if (i >= MAX_CLIENTS) { break; } } close(server_fd); return 0; } ``` 在上面的示例代码中,首先创建了一个服务器套接字,然后绑定到指定的IP地址和端口号。接着,使用`listen`函数监听来自客户端的连接请求。 在主循环中,通过调用`accept`函数接受客户端连接,并且为每个客户端连接创建一个新的线程来处理收发操作。`handle_client`函数是在线程中执行的任务,负责接收客户端数据并发送响应给客户端。 需要注意的是,在多线程编程中,要确保网络资源的正确管理和共享,避免竞争条件和死锁等问题的出现。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值