消息队列
实际上是内核中的一个链表。
- 消息队列提供了从一个进程向另一个进程发送一块数据的方法。
- 每一个数据都被认为有一个类型,接受的进程可以接受多种,单一次只能接受一个自己指定的类型。
- 消息队列最大长度有上限(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的交互
服务器先启动,且7*24小时运行,客户端主动发起请求,服务端被动接收请求,客户端发送的请求不同,服务端会返回不同的结果。
//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;
}
客户端发送请求并等待服务器响应
服务器端接收客户端消息并相应客户端
通过 ipcs -q 查看刚刚创建的消息队列
通过 ipcrm -q [msgid] 删除已有的消息队列
消息队列总结
- 消息队列适用于任何两进程交互。
- 生命周期随内核,进程结束时不会自动销毁,可以使用函数(msgctl)删除,也可以使用命令 ipcrm -q [msqid] 删除。
- 面向数据报,一次发送一个节点,一次也必须接收一个节点。
- 消息队列内嵌同步互斥机制。
- 消息队列是全双工通信。