消息队列
实际上是内核中的一个链表。
- 消息队列提供了从一个进程向另一个进程发送一块数据的方法。
- 每一个数据都被认为有一个类型,接受的进程可以接受多种,单一次只能接受一个自己指定的类型。
- 消息队列最大长度有上限(MSGMAX),每个消息队列的字节有上限(MSGMNB),消息队列的总是有上限(MSGMIN)。
可在Linux下的 /usr/include/linux/msg.h查看消息队列结构
消息队列在内核中的表示
msgget函数 - 创建访问消息队列
int msgget(key_t key,int msgflg);
参数:
key:一个消息队列的名字
msgflg:权限
返回值:
成功返回消息队列的标识码,给后续的msgctl,msgsen,msgrcv使用
失败返回 -1
msgflg:
IPC_CREAT:不存在就创建,存在就打开
IPC_EXCL : 加上这个选项,存在就打开失败
key值的产生:
key_t ftok(const char* pathname,int pro_id);
参数 :
pathname :是一个路径,只要确保是当前系统中一个存在的目录即可
pro_id :表示同一项目中不同的消息队列
返回值:
成功返回非负数
失败返回小于0的值
msgctl
函数 - 控制消息队列
int msgctl(int msqid,int cmd,struct msqid_ds* buf);
参数 :
msqid :由msgget所获得的标识码
cmd : 相应操作
返回值 :失败返回-1,成功返回0
cmd有三值可取,我们最经常用的是 IPC_RMID,意义为删除消息队列,且不需要函数的第三个参数。
msgsnd函数 - 添加一条消息到消息队列中
int msgsnd(int msqid, const void *msgp, size_t msgsz, int msgflg);
参数
msgid: 由msgget函数返回的消息队列标识码
msgp:是⼀个指针,指针指向准备发送的消息
msgsz:是msgp指向的消息⻓度,这个⻓度不含保存消息类型的那个long int⻓整型
msgflg:控制着当前消息队列满或到达系统上限时将要发⽣的事情 msgflg=IPC_NOWAIT表⽰队列满不等待,返回EAGAIN错误。
返回值:成功返回0;失败返回-1
消息结构体的形式如下
struct msgbuf
{
long mtype; // 表示当前消息的类型
char mtext[1]; //表示当前下次的内容,这里的1没有意义,大小需要根据需要来设定
}
msgrcv函数 - 从消息队列接收消息
ssize_t msgrcv(int msqid, void *msgp, size_t msgsz, long msgtyp, int msgflg);
参数
msgid: 由msgget函数返回的消息队列标识码
msgp:是⼀个指针,指针指向准备接收的消息
msgsz:是msgp指向的消息⻓度,这个⻓度不含保存消息类型的那个long int⻓整型
msgtype:它可以实现接收优先级的简单形式
msgflg:控制着队列中没有相应类型的消息可供接收时将要发⽣的事
返回值:成功返回实际放到接收缓冲区⾥去的字符个数,失败返回-1
代码实现service和client的交互
//com.h 此次交互所涉及的动作 #pragma once #include <sys/types.h> #include <sys/ipc.h> #define CLIENT_TYPE 1 #define SERVER_TYPE 2 typedef struct msgbuf { long mtype; char mtest[128]; }msgbuf; int CreateMsgQueue(); int OpenMsgQueue(); void DestoryMsgQueue(); void SendMsgQueue(int msgid,int type,char* msg); void ReceiveMsgQueue(int msgid,int type,char out[]);
//com.c #include "com.h" #include <stdio.h> #include <stdlib.h> #include <sys/msg.h> #include <string.h> int Commend(int flags) { key_t key = ftok(".",1); if(key < 0) { perror("ftok"); exit(1); } int msgid = msgget(key,flags); if(msgid < 0) { perror("magget"); exit(1); } return msgid; } int CreateMsgQueue() { return Commend(IPC_CREAT | IPC_EXCL | 0666); } int OpenMsgQueue() { return Commend(IPC_EXCL); } void DestoryMsgQueue() { int msgid = OpenMsgQueue(); if((msgctl(msgid,IPC_RMID,NULL)) < 0) { perror("msgctl"); exit(1); } } void SendMsgQueue(int msgid,int type,char* msg) { msgbuf buf; buf.mtype = type; strcpy(buf.mtest,msg); if(msgsnd(msgid,&buf,sizeof(buf.mtest),0) < 0) { perror("msgsnd"); exit(1); } } void ReceiveMsgQueue(int msgid,int type,char out[]) { msgbuf buf; if(msgrcv(msgid,&buf,sizeof(buf.mtest),type,0) < 0) { perror("msgrcv"); exit(1); } strcpy(out,buf.mtest); }service接收客户端发来的消息,并对其进行相应
//service.c #include "com.h" #include <stdio.h> #include <unistd.h> int main() { int msgid = CreateMsgQueue(); char buf[1024] = {0}; while(1) { ReceiveMsgQueue(msgid,CLIENT_TYPE,buf); printf("%s\n",buf); printf("server >"); fflush(stdout); read(0,buf,sizeof(buf)); SendMsgQueue(msgid,SERVER_TYPE,buf); } DestoryMsgQueue(); return 0; }
client想服务器发送消息,并相应服务器发回的消息
#include "com.h" #include <stdio.h> #include <unistd.h> int main() { int msgid = OpenMsgQueue(); char buf[1024] = {0}; while(1) { printf("client > "); fflush(stdout); read(0,buf,sizeof(buf)); SendMsgQueue(msgid,CLIENT_TYPE,buf); ReceiveMsgQueue(msgid,SERVER_TYPE,buf); printf("%s\n",buf); } return 0; }
客户端发送请求并等待服务器响应
服务器端接收客户端消息并相应客户端
消息队列总结
- 消息队列适用于任何两进程交互。
- 生命周期随内核,进程结束时不会自动销毁,可以使用函数(msgctl)删除,也可以使用命令 ipcrm -q [msqid] 删除。
- 面向数据报,一次发送一个节点,一次也必须接收一个节点。
- 消息队列内嵌同步互斥机制。
- 消息队列是全双工通信。