消息队列的公共资源是链表结构。
通信双方不会和消息队列进行挂接,而是像管道一样,访问内存中的消息队列。
消息队列由操作系统维护,但是由通信的某一方创建和删除
通信双方都需要获取到消息队列,和共享内存一样。
当发送方有数据发送时,将数据先打包成一个节点,然后尾插到内核中的消息队列中去。
当接收方接收数据时,从队列头部开始去找所需要的节点,然后进行解包得到数据。
消息队列和普通队列不一样,不是严格按照先进先出的规则。读取方可以跳过队头寻找自己需要的数据。但是相同的数据,必须先读取靠近队头的。
如上图中,当读取方需要的是香蕉,但是队头是苹果,此时就可以跳过苹果,读取香蕉,并且靠近队头的香蕉先被读取。
2.消息队列相关函数
msgget()
int msgget(key_t ,int msflg)
key:和共享内存一样,也需要生成,是消息队列唯一性的标识符。msgflg:和共享内存一样,可以是IPC_CREAT或者IPC_EXCL或者是二者的组合。返回值:返回消息队列的标识符,供用户层使用。
msgctl()
int msgctl(int msgid,int cmd,struct msqid_dsbuf)
参数和共享内存的shmctl一样。
**第一个成员变量的类型是struct ipc_perm*,变量名是msg_perm,结构类型和共享内存的一样。**
msgsnd()
int msgsnd(int msgid,const void*msgp,size_t msgsz,int msgflg)
msgid:消息队列标识符
msgp:要发送数据所在的数组,元素类型是 struct msgbuf
msgsz:要发生的数据大小,以字节为单位
msgflg:创建标记,如果使用IPC_NOWAIT,失败就会立即返回。
0:阻塞发送
IPC_NOWAIT:非阻塞发送
返回值:失败返回-1,成功返回0
在发送数据之前,先对数据进行打包
struct msgbuf
{
long mtype; //数据类型,必须大于0
char mtext[1];//要发送的数据
};
msgrcv()
ssize_t msgrcv(int msgid,void*msgp,size_t msgsz,long msgtyp,int msgflg)
msgid:消息队列标识符
msgp:接收的数据后要存放的地址
msgsz:要接收的数据大小
msgtype:发送方设定的数据类型标识
0:读取队列中的第一条消息(不在乎当前队列头元素是什么消息类型,将他当作普通队列来处理)。
大于0(约定值):读取队列中类型为msgtyp 的第一条消息。(就是读取对列元素中第一个香蕉)
小于0:读取队列中最小类型小于或等于msgtyp 绝对值的第一条消息。
msgflg:创建标记,如果指定IPC_ NOWAIT,获取失败会立刻返回
0:阻塞接收
NOWAIT:非阻塞接收
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
#include <stdio.h>
#include <string.h>
// 定义消息结构体
struct msbuf {long mytype; // 消息类型char mtext[50]; // 消息内容
};
int main() {key_t key = ftok(".", 'a'); // 获取消息队列的键值int msqid = msgget(key, IPC_CREAT | 0666); // 创建消息队列struct msbuf buf; // 声明消息结构体// 发送两条消息,类型分别为1和2strcpy(buf.mtext, "Hello from type 1 message!");buf.mytype = 1;msgsnd(msqid, &buf, sizeof(struct msbuf) - sizeof(long), 0); // 发送消息strcpy(buf.mtext, "Hello from type 2 message!");buf.mytype = 2;msgsnd(msqid, &buf, sizeof(struct msbuf) - sizeof(long), 0); // 发送消息// 接收消息,并根据类型进行处理for (int i = 0; i < 2; i++) {msgrcv(msqid, &buf, sizeof(struct msbuf) - sizeof(long), 0, 0); // 接收消息if (buf.mytype == 1) {printf("Received type 1 message: %s\n", buf.mtext);} else if (buf.mytype == 2) {printf("Received type 2 message: %s\n", buf.mtext);} else {printf("Received unknown type message: %s\n", buf.mtext);}}// 删除消息队列msgctl(msqid, IPC_RMID, NULL);return 0;
}