消息队列是进程间通信System V版本(消息队列、信号量、共享内存)之一。
所谓System V版本就是其生命周期随内核(及时进程结束,消息队列也不会被删除),这是与管道的不同。
还有一个不同点就是:管道是以数据流方式来发送(接受)数据,而消息队列是以数据块的方式来发送(接受)数据。
首先,下面是有关消息队列的系统调用:
1、int msgget(key_t key, int msgflg); //成功返回消息队列的编号,失败返回-1
作用:
用来创建或者获取一个消息队列
头文件:
#include <sys/types.h>
#include <sys/ipc.h>
#incldue <sys/msg.h>
参数:
第一个参数是一个唯一的key值。其一般由ftok函数生成。
key_t ftok(const char* pathname, int proj_id); //成功返回0,失败返回-1.
第一个参数一般使用当前文件的路径或文件名,第二个参数一般给一个整数(其实,随意给一个就可以,ftok内部会通过这两个参数经过某种运算生成一个唯一的key值来标识该消息队列)
第二个参数为一般为:IPC_CREAT和IPC_EXCL组合或单独使用,如要使用多个,只需要用“|”(或)链接起来。
1>、IPC_CREAT:如果消息队列不存在,则创建之,并返回消息队列的编号,如果存在,则返回当前现有的消息队列编号。
2>、IPC_CREAT|IPC_EXCL:如果消息队列存在则出错返回,否则创建之。(意味着,一定可以保证创建一个新的消息队列)
3>、IPC_EXCL:该选项一般不单独使用,单独使用没有任何意义。
4>、IPC_CREAT|IPC_EXCL|0666 在2的基础上可以设置该消息队列的权限为0666。
2、int msgctl(int msgid,int cmd,struct msgid_ds *buf); //失败返回-1,成功返回0(IPC_STAT/IPC_SET/IPC_RMID)
作用:
用来对消息队列执行一些操作(包括删除操作)
参数:
第一个参数为消息队列的编号msgid;
第二个参数为属性,用来告诉操作系统要执行什么操作,比如(IPC_RMID删除、IPC_STAT查看消息信息)
第三个参数为辅助参数。
IPC_RMID:可以将该参数设置为NULL,因为不关心该结构体的信息(消息队列用一个叫msgid_ds的结构体来描述)。
IPC_STAT:该参数为一个输出型参数,来获取msgid_ds这个结构体。
3、int msgsnd(int msgid, const void* ptr,size_t bytes,int flag);//成功返回0,失败返回-1
作用:
向消息队列发送数据。
参数:
第一个参数为消息队列的id;
第二个参数为要发送的数据内容,需要用到一个结构体(struct msgbuf)来发送数据;(需要以块为单位发送数据)
第三个参数为要发送数据的大小(以字节为单位);
第四个参数为标志位,代表该消息以什么方式来发送。当其值为0时,代表以阻塞方式发送数据。
4、ssize_t msgrcv(int msgid, void* ptr,size_t bytes,long msgtype,int flag);
作用:
接受消息。
参数:
很明显的可以看到,比msgsnd多了一个参数,就是long msgtype,为什么要多这个参数呢?
其实,大家想想也很简单,既然是以数据块的方式发送数据,那么也应该要以数据块的方式接受,而这个就是表示你要收的数据是那个类型的。就像你买东西的时候,先要知道要买什么东西。
5、struct msgbuf
{
long mtype;
char mtext[1];
}
首先要声明的是:该结构体需要自己定义。
第一个成员就是我们上边说的msgtype,标明该被发送(接受)的数据是什么类型(这里的类型不是变量的类型,是给数据块自定义一个名字)。
第二个成员是一个字符数组,大小在定义的时候可以自行设定。
好!那么接下来就演示一下,如何使用消息队列来完成进程间的通信。
首先要得知道,如何使用Linux命令查询当前的消息队列。
ipcs -q 查看消息队列(如果在普通用户下查看不到,切换到root下就可以看到)。
ipcrm -q msqid号 删除该消息队列。
代码:共有4个文件:comm.h、comm.c、server.c、client.c
1、comm.h
#ifndef __COMM_H__
#define __COMM_H__
#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/msg.h>
#include <sys/ipc.h>
#include <string.h>
#define MAX_TEXT 1024 //自定义发送数据的最大大小为1024字节
#define SERVER 1
#define CILENT 2
struct msgbuf
{
long mtype;
char mtext[MAX_TEXT];
};
//对系统调用进行封装
int CreatMsgQueue();
int GetMsgQueue();
int SendMsg();
int RcvMsg();
int DestoryMsgQueue();
#endif // __COMM_H__
2、comm.c
#include "comm.h"
int CommMsgQueue(int flgs)
{
key_t key = ftok("./comm.h",0);
int msgid = 0;
if((msgid = msgget(key,flgs)) == -1)
{
perror("msgget");
return -1;
}
return msgid;
}
int CreatMsgQueue()
{
return CommMsgQueue(IPC_CREAT|IPC_EXCL|0666);
}
int GetMsgQueue()
{
return CommMsgQueue(IPC_CREAT);
}
int SendMsg(int msgid,char* data,int name)
{
struct msgbuf mb;
mb.mtype = name;
strcpy(mb.mtext, data);
if(msgsnd(msgid,&mb,MAX_TEXT,0) == -1)
{
perror("msgsnd");
return -1;
}
return 0;
}
int RcvMsg(int msgid,int name,struct msgbuf* buf)
{
if(msgrcv(msgid,buf,MAX_TEXT,name,0)==-1)
{
perror("msgrcv");
return -1;
}
return 0;
}
int DestoryMsgQueue(int msgid)
{
if(msgctl(msgid,IPC_RMID,NULL) == -1)
{
perror("msgctl");
return -1;
}
return 0;
}
3、server.c
#include "comm.h"
//server.c
int main()
{
int msgid = CreatMsgQueue();
while(1)
{
char msg[MAX_TEXT];
printf("server:");
fflush(stdout);
int s = read(0,msg,sizeof(msg));
if(s<0)
{
perror("read");
return 1;
}
msg[s-1]=0;
SendMsg(msgid,msg,SERVER);
struct msgbuf buf;
RcvMsg(msgid,CILENT,&buf);
printf("cilent:%s\n",buf.mtext);
}
DestoryMsgQueue(msgid);
return 0;
}
4、client.c
#include "comm.h"
//client.c
int main()
{
int msgid = GetMsgQueue();
while(1)
{
struct msgbuf buf;
RcvMsg(msgid,SERVER,&buf);
printf("server:%s\n",buf.mtext);
char msg[MAX_TEXT];
printf("cilent:");
fflush(stdout);
int s = read(0,msg,sizeof(msg));
if(s<0)
{
perror("read");
return 1;
}
msg[s-1]=0;
SendMsg(msgid,msg,CILENT);
}
return 0;
}
5、运行结果