C语言消息队列传输数组实现及收发逻辑问题咨询
C语言System V消息队列传输数组及同步问题解答
核心问题定位
你当前代码无法传输数组的直接原因有三个:
- 调用
msgsnd时第三个参数只传入了sizeof(msg.msgtext),数组所在的内存块根本没有被写入消息队列 - 接收端定义的消息结构体和发送端内存布局不一致,没有预留数组成员的内存空间,就算发了数组也会因为内存错位读不到
- 代码里调用
wait(NULL)但没有提前fork()子进程,该函数调用无实际作用,单进程内同时写收发逻辑不符合消息队列的进程间通信设计逻辑
疑问1:消息队列发送端与接收端是否需要分离实现?
必须分离实现。
System V消息队列是跨进程通信组件,设计目标就是在两个独立进程间传递数据。收发两端需要满足两个基本要求:
- 两端定义的消息结构体内存布局完全一致:第一个成员固定为
long类型的消息类型字段,后续自定义载荷(字符串、数组等)的类型、长度定义必须完全匹配,否则会出现数据读取错位 - 两端通过同一个消息队列key(你代码里用的1234)访问同一条消息队列
疑问2:3次数组按序发送的同步逻辑实现
用消息类型做双向握手机制即可,不需要额外的锁或者信号量,逻辑简单可靠:
- 约定消息类型规则:发送端发数组数据、结束通知都用类型1,接收端处理完一批数据后回传的ACK确认消息用类型2
- 发送端流程:每发完1次数组,就阻塞等待接收端回传的类型2ACK消息,只有收到确认才启动下一批数组的生成和发送,从流程上保证发送顺序
- 接收端流程:每收到1次类型1的数组消息,先判断是不是结束通知,不是的话完成数据读取处理后立刻回1个类型2的ACK
- 收发同步场景下不要用
IPC_NOWAIT非阻塞模式,直接用默认阻塞模式即可,避免空轮询或者读不到数据的误报错
数组传输实现逻辑
消息队列的收发本质是内存块拷贝,传输int数组和传输char字符串没有区别:只要把数组作为消息结构体的载荷成员,msgsnd时传入载荷部分的总长度(即结构体总长度减去第一个long类型消息字段的长度),接收端用匹配的结构体接收,就能直接读到数组内容。
可运行参考代码
发送端 sender.c
#include <stdio.h> #include <stdlib.h> #include <string.h> #include <sys/msg.h> #include <time.h> #define maxArr 100 #define MSG_KEY 1234 // 收发两端必须保持该结构体定义完全一致 struct msgbuffer { long msgtype; char msgtext[100]; int arr[maxArr]; int arr_len; // 额外传数组实际长度,方便接收端处理 }; // ACK消息结构体 struct ack_msg { long msgtype; char status[10]; }; int main() { int msgid = msgget(MSG_KEY, IPC_CREAT | 0600); if (msgid == -1) { perror("msgget failed"); exit(1); } srand(time(NULL)); struct msgbuffer data_msg; struct ack_msg ack; for (int batch = 0; batch < 3; batch++) { // 填充当前批次数据 data_msg.msgtype = 1; sprintf(data_msg.msgtext, "第%d批数组", batch+1); data_msg.arr_len = 10; // 每批发10个随机数 for (int i = 0; i < data_msg.arr_len; i++) { data_msg.arr[i] = rand() % 101; } // 发送数组:长度为结构体减去long msgtype的大小 if (msgsnd(msgid, &data_msg, sizeof(data_msg) - sizeof(long), 0) == -1) { perror("msgsnd failed"); exit(1); } printf("已发送第%d批数组,等待接收端确认...\n", batch+1); // 阻塞等ACK,收到才发下一批 if (msgrcv(msgid, &ack, sizeof(ack) - sizeof(long), 2, 0) == -1) { perror("receive ack failed"); exit(1); } printf("收到接收端确认,开始发送下一批\n"); } // 发结束通知 data_msg.msgtype = 1; strcpy(data_msg.msgtext, "finish"); msgsnd(msgid, &data_msg, sizeof(data_msg) - sizeof(long), 0); return 0; }
接收端 receiver.c
#include <stdio.h> #include <stdlib.h> #include <string.h> #include <sys/msg.h> #define maxArr 100 #define MSG_KEY 1234 // 和发送端结构体完全一致 struct msgbuffer { long msgtype; char msgtext[100]; int arr[maxArr]; int arr_len; }; struct ack_msg { long msgtype; char status[10]; }; int main() { int msgid = msgget(MSG_KEY, 0600); if (msgid == -1) { perror("msgget failed"); exit(1); } struct msgbuffer data_msg; struct ack_msg ack; while (1) { // 阻塞收类型1的消息 ssize_t ret = msgrcv(msgid, &data_msg, sizeof(data_msg) - sizeof(long), 1, 0); if (ret == -1) { perror("msgrcv failed"); exit(1); } // 收到结束消息就退出 if (strcmp(data_msg.msgtext, "finish") == 0) { printf("收到结束通知,退出接收\n"); msgctl(msgid, IPC_RMID, NULL); // 删除消息队列 break; } // 处理数组 printf("收到%s,内容为:", data_msg.msgtext); for (int i = 0; i < data_msg.arr_len; i++) { printf("%d ", data_msg.arr[i]); } printf("\n"); // 回ACK ack.msgtype = 2; strcpy(ack.status, "ok"); msgsnd(msgid, &ack, sizeof(ack) - sizeof(long), 0); } return 0; }
运行方式
分别编译两个文件,先启动接收端,再启动发送端即可看到按序传输的效果:
gcc receiver.c -o receiver gcc sender.c -o sender ./receiver & ./sender
内容的提问来源于stack exchange,提问作者snoopy
相关产品推荐
相关产品推荐

