版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/KentZhang_/article/details/48198427
消息队列是Linux进程间通信方式之一,在面向对象编程中,需要对其封装。
一、消息队列的特点
1、异步通信,消息队列会保存进程发送的消息,其他进程不一定要及时取走消息。
2、可以发送不同类型的消息,消息的头部用long类型的字段标记。
3、取消息时,不一定按先进先出的方式,可以按消息的类型来取。
4、消息队列内部是用一个链表来保存消息,当消息被取走后,这个消息就从链表中删除了,而且这个链表的长度
是有限制的,当消息存满后,不能再存入消息。
二、msgid和关键字
消息队列在内核中,要用一个非负整数来标记(类似于描述符或者句柄),这个非负整数称作msqid,即消息
队列的ID,但是要创建或者打开一个消息队列需要一个关键字,这个关键字其实是一个长整型数据。在进程
通信时,必须要约定使用同一个关键字,这样就可以得到同一个消息队列,因为消息队列是由内核维护的,
不同的进程使用相同关键字打开或者创建的消息队列,获得的msgid是相同的。
一、消息队列的特点
1、异步通信,消息队列会保存进程发送的消息,其他进程不一定要及时取走消息。
2、可以发送不同类型的消息,消息的头部用long类型的字段标记。
3、取消息时,不一定按先进先出的方式,可以按消息的类型来取。
4、消息队列内部是用一个链表来保存消息,当消息被取走后,这个消息就从链表中删除了,而且这个链表的长度
是有限制的,当消息存满后,不能再存入消息。
二、msgid和关键字
消息队列在内核中,要用一个非负整数来标记(类似于描述符或者句柄),这个非负整数称作msqid,即消息
队列的ID,但是要创建或者打开一个消息队列需要一个关键字,这个关键字其实是一个长整型数据。在进程
通信时,必须要约定使用同一个关键字,这样就可以得到同一个消息队列,因为消息队列是由内核维护的,
不同的进程使用相同关键字打开或者创建的消息队列,获得的msgid是相同的。
本例中使用一个文件路径加上课题ID(0~255)调用ftok函数产生一个关键字。
代码如下:
CMsgQueue.h
/*************************************************************************
> File Name: CMsgQueue.h
> Author: KentZhang
> Mail: [email protected]
> Created Time: Wed 02 Sep 2015 08:10:35 PM CST
************************************************************************/
#ifndef _CLASSES_CMSGQUEUE_H__
#define _CLASSES_CMSGQUEUE_H__
#include <sys/types.h>
#include <sys/msg.h>
#include <unistd.h>
#include <sys/ipc.h>
#include <sys/msg.h>
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <string.h>
//封装消息队列
class CMsgQueue
{
public:
CMsgQueue(const char* path, int id);
~CMsgQueue();
bool MsgGet();
bool MsgCreat();
bool MsgSnd(const void *msg, size_t nbytes, int flag);
bool MsgRcv(void *msg, size_t nbytes, long type, int flag);
int GetMsgQueueID();
bool Destroy();
private:
int m_MsgQueueID;//消息队列的ID
key_t m_MsgKey; //创建消息队列需要的关键字
};
#endif
CMsgQueue.cpp
/*************************************************************************
> File Name: CMsgQueue.cpp
> Author: KentZhang
> Mail: [email protected]
> Created Time: Wed 02 Sep 2015 08:20:14 PM CST
************************************************************************/
#include "CMsgQueue.h"
#ifndef RELEASE
#define DEBUG_ERROR(format,...) do{ printf(""format", FileName:%s, FuncName:%s, LineNum:%d\n",\
##__VA_ARGS__, __FILE__, __func__, __LINE__);}while(0)
#else
//当软件release时,这个宏应该把出错信息写入文件中,当然要考虑多线程的情况,此处代码省略
#define DEBUG_ERROR(format,...) do{ /*此处代码省略*/ }while(0)
#endif
//构造函数中只是产生一个关键字
CMsgQueue::CMsgQueue(const char* path, int id)
{
m_MsgKey = ftok(path, id);
if (m_MsgKey < 0 )
{
DEBUG_ERROR("CMsgQueue() ftok(%s, %d) failed:%s", path, id, strerror(errno));
}
m_MsgQueueID = -1;
}
CMsgQueue::~CMsgQueue()
{
//在析构函数中,如果调用msgctl函数删除消息队列,
//那么有可能别的进程正在使用,因此谁创建,应该由
//谁调用Destory函数删除
}
//打开或者新建一个消息队列
bool CMsgQueue::MsgGet()
{
m_MsgQueueID = msgget(m_MsgKey, IPC_CREAT|0666);
if(-1 == m_MsgQueueID)
{
DEBUG_ERROR("CMsgQueue::MsgGet() failed:%s", strerror(errno));
return false;
}
return true;
}
//新创建消息队列
//有时候进程非正常退出时,消息队列没有删除,如果里面还有消息,
//将对程序的下一次运行产生影响,下面的函数可保证是新创建的消息队列
bool CMsgQueue::MsgCreat()
{
int nQueueID = msgget(m_MsgKey, IPC_CREAT|IPC_EXCL|0666);
if(-1 == nQueueID)
{
this->MsgGet();
msgctl(m_MsgQueueID, IPC_RMID, NULL);
m_MsgQueueID = 0;
return this->MsgGet();
}
m_MsgQueueID = nQueueID;
return true;
}
//发送消息
bool CMsgQueue::MsgSnd(const void *msg, size_t nbytes, int flag)
{
int nResult = msgsnd(m_MsgQueueID, msg, nbytes, flag);
if( -1 == nResult)
{
DEBUG_ERROR("CMsgQueue::msgSnd() failed:%s", strerror(errno));
return false;
}
return true;
}
//接收消息
bool CMsgQueue::MsgRcv(void *msg, size_t nbytes, long type, int flag)
{
int nResult = msgrcv(m_MsgQueueID,msg, nbytes, type, flag);
if( -1 == nResult)
{
DEBUG_ERROR("CMsgQueue::msgRcv() failed:%s", strerror(errno));
return false;
}
return true;
}
//获得消息队列ID
int CMsgQueue::GetMsgQueueID()
{
return m_MsgQueueID;
}
//删除消息队列
bool CMsgQueue::Destroy()
{
int nResult = msgctl(m_MsgQueueID, IPC_RMID, NULL);
if(-1 == nResult)
{
DEBUG_ERROR("CMsgQueue::Destroy() failed:%s", strerror(errno));
return false;
}
return true;
}
由于笔者的水平有限,出错在所难免,恳请读者拍砖指正,谢谢阅读。