消息队列
消息队列提供了一种从一个进程向另一个进程发送一个数据块的方法。 每个数据块都被认为含有一个类型,接收进程可以独立地接收含有不同类型的数据结构。我们可以通过发送消息来避免命名管道的同步和阻塞问题。但是消息队列与命名管道一样,每个数据块都有一个最大长度的限制。
注意:
因为管道是随进程的,进程结束管道生命周期也就结束,而对于消息队列来说,是随操作系统的,就算进程突出,不去手动的释放消息队列,消息队列依然是存在的。
我们可以使用ipcs -q进行查看操作系统的消息队列,使用ipcrm -q msgid进行销毁消息队列。
消息队列的创建
使用msgget函数,我们可以创建或者访问一个消息队列,
在创建消息队列之前我们依然要用ftok得到一个key标识符。
key_t ftok(const char *pathname, int proj_id);
这里的pathname是路径,proj_id用来指定数字,为生成唯一的key提供。该函数把从pathname导出的信息与id的低序8位组合成⼀个整数IPC键。
然后创建队列。
int msgget(key_t key, int msgflg);
msgflg使用IPC_CREATE和IPC_EXCL进行创建消息队列,当msgflg使用IPC_CREATE和0的时候,这个时候使用消息队列。
函数返回值返回一个msgid,失败返回-1。
发送和接收消息
使用消息队列当然涉及到发送消息和接受消息,msgsnd函数和msgrcv函数提供了这两个功能,
首先来看msgsnd函数
int msgsnd(int msqid, const void *msgp, size_t msgsz, int msgflg);
在这里我们需要关注第二个参数,第二个参数是指向一个结构体的一个指针,这个结构体叫做 struct msgbuf
struct msgbuf {
long mtype; /* message type, must be > 0 */
char mtext[用户指定大小]; /* message data */
};
这个结构体第一个变量是消息的类型,第二个是存放消息的数组。
这个结构体早sys/msg.h当中已经有定义,所以我们可以直接拿来用就行。
然后看msgsnd的后面两个参数:
msgsz是注意是消息的长度,而不是整个结构体的长度,也就是说msg_sz是不包括长整型消息类型成员变量的长度,是指第二个参数制作指向的消息长度。
msgflg,用来指明在没有数据的情况下所需要做的操作,这个操作我们经常使用IPC_NOWAIT,这样当消息队列满了的时候,再向消息队列中发送消息,不会发生阻塞现象。
当调用成功,这个时候消息数据将被拷贝放入到消息队列中
接收消息:
ssize_t msgrcv(int msqid, void *msgp, size_t msgsz, long msgtyp, int msgflg);
msgrcv函数第二个参数,同样是指向消息队列结构体的指针,第三个参数和第四个也和上述是一样的。
当调用成功的时候,返回消息队列接受缓存当中的字节数,消息复制到用户缓冲区,然后删除消息队列当中的对应的信息。
控制消息队列
控制消息队列需要使用的函数是msgctl函数,
int msgctl(int msqid, int cmd, struct msqid_ds *buf);
这个函数中的cmd参数提供需要的操作,例如使用IPCRMID就可以进行删除消息队列。
例子:
代码实现:
//comm.h
#ifndef _COMM_H_
#define _COMM_H_
#include<stdio.h>
#include<stdlib.h>
#include<sys/ipc.h>
#include<sys/types.h>
#include<sys/msg.h>
#include<string.h>
#define FILENAME "."//代表但当前路径
#define ID 0x6666
#define SERVER_TYPE 1
#define CLIENT_TYPE 2
struct _msgbuf
{
long mtype;
char mtext[1024];
};
int creatMsgQueue();//消息队列的创建
int getMsgQueue();//获取消息队列
int destoryMsgQueue(int msgid);//消息队列的删除
int sendMsg(int msgid,int type,const char*msg);//发送消息
int recvMsg(int msgid,int type,char *out);//接收消息
#endif
//comm.c
#include"comm.h"
static int commMsgQueue(int flags)
{
key_t _key = ftok(FILENAME,ID);//获取key值
if(_key<0)
{
printf("ftok");
return -1;
}
int msgid = msgget(_key,flags);
if(msgid<0)
{
printf("msgget");
return -2;
}
return msgid;
}
int creatMsgQueue()
{
return commMsgQueue(IPC_CREAT | IPC_EXCL|0666);
}
int getMsgQueue()
{
return commMsgQueue(IPC_CREAT);
}
int destoryMsgQueue(int msgid)
{
if(msgctl(msgid,IPC_RMID,NULL)<0)
{
perror("msgctl");
return -1;
}
return 0;
}
int sendMsg(int msgid,int type,const char*msg)
{
struct _msgbuf _mb;
_mb.mtype = type;
strcpy(_mb.mtext,msg);
if(msgsnd(msgid,&_mb,sizeof(_mb.mtext),0)<0)
{
perror("msgsnd");
return -1;
}
return 0;
}
int recvMsg(int msgid,int type,char *out)
{
truct _msgbuf _mb;
if(msgrcv(msgid,&_mb,sizeof(_mb.mtext),type,0)<0)
{
perror("msgrcv");
return -1;
}
strcpy(out,_mb.mtext);
return 0;
}
//client.c
#include"comm.h"
int main()
{
int msgid = getMsgQueue();
char buf[1024];
while(1)
{
buf[0] = 0;
printf("client Enter#");
fflush(stdout);
ssize_t s = read(0,buf,sizeof(buf)-1);
if(s>0)
{
buf[s-1] = 0;
sendMsg(msgid,CLIENT_TYPE,buf);
}
recvMsg(msgid,SERVER_TYPE,buf);
printf("server say# %s\n",buf);
}
return 0;
}
//server.c
#include"comm.h"
int main()
{
int msgid = creatMsgQueue();//creat msg
char buf[1024];
while(1)
{
buf[0] = 0;
recvMsg(msgid,CLIENT_TYPE,buf);
printf("client say# %s\n",buf);
printf("please Enter# ");
fflush(stdout);//使上面那句话刷新到屏幕
ssize_t s = read(0,buf,sizeof(buf)-1);
if(s>0)
{
buf[s-1] = 0;
sendMsg(msgid,SERVER_TYPE,buf);
}
}
destoryMsgQueue(msgid);
return 0;
}