消息队列简介
前两篇博客我们讲述了利用匿名管道与命名管道来实现进程间通信的方法。这里我们再对消息队列进行讲述。消息队列,通俗来讲就是一堆消息组合在一起的队列,也就是一个个数据块,并且这些数据块是有类型的,而我们不同进程通过一个消息队里的标记,查询到同一个消息队列,此时的消息队列就相当于一片公共资源(临界资源),这时候通过这个消息队列就可以达到我们进程间的通信。但是消息队列中每个消息的最大长度都是有上限的,每个消息队列总的字节数大小也是由上限的,操作系统中可维护的消息队列的总数也是由上限的。
在操作系统中,其实有很多消息队列,而想要通信的进程之间一定会找到一个相同的消息队列,而这个消息队列一定拥有自己与其他消息队列不同点,这就是消息队列的名字,也叫作key,每个消息队列拥有自己的key。
消息队列函数
函数msgget
功能:创建和访问一个消息队列
原型:int msgget(key_t key, int msgflg);
参数:key
消息队列的名字。
msgflg
由九个权限标志构成,用法与创建文件时使用的mode模式标志是一样的。
返回值:成功返回一个非负整数,即该消息队列的标识码,失败返回-1
这里msgflg有两个选项:IPC_CREAT与IPC_EXCL
函数msgctl
原型:int msgctl(int msgid, int cmd, struct msgid_ds* buf);
参数:msgid
由msgget
函数返回的消息队列的标识码
cmd
是将要采取的动作
返回值:如果成功返回0,失败返回-1
其中cmd
的选项为:IPC_STAT:把msqid_ds结构中的数据设置为消息队列的当前关联值
IPC_SET:在进程有足够权限的前提下,把消息队列的当前关联值设置为msqid_ds数据结构中给的值
IPC_RMID:删除消息队列
函数msgsnd
原型:int msgsnd(int msqid, const void* msgp, size_t msgsz, int msgflg);
msgid:有msgget函数返回的消息队列的标识符
参数:
msgp:是一个指针,指针指向准备发送的消息
msgsz:是指针指向消息的长度,这里不包含保存消息类型的long int长整型
msgfig:IPC_NOWAIT表示消息队列满不等待,返回EAGAIN错误
返回值:成功返回0,失败返回-1
这里我们要发送的消息应当储存到一个结构体里面去
struct msgbuf{
long mtype;
char mtext[1];
};
其中long mtype存储类型信息,而mtext存储要发送的实际内容。
函数`msgrcv
功能:从一个消息队列接收消息
原型: ssize_t msgrcv(int msgqid, void* msgp, size_t msgsz, long msgtyp, int msgflg);
参数:
msgid:由msgget函数返回的队列标识符
msgp:是一个指针,指针指向准备接收的消息
msgsz:是msgp指向消息的长短,不包含保存消息类型的那个 long int长整型
msgtype:可是实现接收优先级的简单形式
msgflg:控制着队列中没有相应类型的消息可供接收时将要发生的事情
返回值:成功返回实际接收放到缓冲区内的字符个数,失败返回-1
msgflg=IPC_NOWAIT:当消息队列内没有可以读取的消息时,不等待直接返回ENOMSG错误
msg=MSG_NOERROR:当消息队列大小超过msgsz的时候被截断
接下来我们就利用消息队列的这些调用接口来实现一个进程间通信。
//comm.h
#pragma once
#include <stdio.h>
#include <sys/ipc.h>
#include <sys/msg.h>
#include <string.h>
#include <sys/types.h>
#include <unistd.h>
#define PATHNAME "."
#define PROJ_ID 0x6666
#define SERVERTYPE 1
#define CLIENTTYPE 2
struct msgbuf1{
long mtype;
char mtext[1024];
};
int creatMsgQueue();
int getMsgQueue();
int destroyMsgQueue(int msgqid);
int sendMsg(int msgid, int whotype, char* msg);
int recvMsg(int msgid, int recvwhotype, char out[]);
//comm.c
#include "comm.h"
int commMsgQueue(int flag)
{
key_t key = ftok(PATHNAME, PROJ_ID);
if(key < 0)
{
printf("ftok error!\n");
return -1;
}
int msgid = msgget(key, flag);
if(msgid < 0)
{
printf("msgget error!\n");
return -1;
}
return msgid;
}
int creatMsgQueue()
{
return commMsgQueue(IPC_CREAT | IPC_EXCL | 0666);
}
int getMsgQueue()
{
return commMsgQueue(IPC_CREAT);
}
int destroyMsgQueue(int msgid)
{
if(msgctl(msgid, IPC_RMID, NULL) < 0)
{
printf("msgctl error!\n");
return -1;
}
return 0;
}
int sendMsg(int msgid, int whotype, char* msg)
{
struct msgbuf1 buf;
buf.mtype = whotype;
strcpy(buf.mtext,msg);
if(msgsnd(msgid, (void*)&buf, sizeof(buf.mtext), 0) < 0)
{
printf("msgsnd error!\n");
return -1;
}
return 0;
}
int recvMsg(int msgid, int recvwhotype, char out[])
{
struct msgbuf1 buf;
if(msgrcv(msgid, (void*)&buf, sizeof(buf.mtext), recvwhotype, 0) < 0)
{
printf("msgrcv error!\n");
return -1;
}
strcpy(out,buf.mtext);
return 0;
}
//server.c
#include "comm.h"
int main()
{
int msgid = creatMsgQueue();
char buf[1024];
while(1)
{
printf("please wait client...\n");
recvMsg(msgid, CLIENTTYPE, buf);
printf("client says# %s",buf);
printf("server says#");
ssize_t s = read(0, buf, sizeof(buf)-1);
if(s < 0)
{
printf("read error!\n");
return -1;
}
else if(s > 0)
{
sendMsg(msgid, SERVERTYPE, buf);
printf("send done,wait recv...\n");
}
}
destroyMsgQueue(msgid);
return 0;
}
//client.c
#include "comm.h"
int main()
{
int msgid = getMsgQueue();
char buf[1024];
while(1)
{
printf("please enter#");
fflush(stdout);
ssize_t s = read(0, buf, sizeof(buf)-1);
if(s < 0)
{
printf("read error!\n");
return -1;
}
else if(s > 0)
{
buf[s] = 0;
sendMsg(msgid, CLIENTTYPE, buf);
printf("send done,wait recv...\n");
}
recvMsg(msgid, SERVERTYPE, buf);
printf("server says# %s",buf);
}
return 0;
}
欢迎大家共同讨论,如有错误及时联系作者指出,并改正。谢谢大家!