您的位置:首页 > 运维架构 > Linux

C++封装Linux消息队列

2015-09-03 21:41 453 查看
消息队列是Linux进程间通信方式之一,在面向对象编程中,需要对其封装。

一、消息队列的特点

1、异步通信,消息队列会保存进程发送的消息,其他进程不一定要及时取走消息。

2、可以发送不同类型的消息,消息的头部用long类型的字段标记。

3、取消息时,不一定按先进先出的方式,可以按消息的类型来取。

4、消息队列内部是用一个链表来保存消息,当消息被取走后,这个消息就从链表中删除了,而且这个链表的长度

是有限制的,当消息存满后,不能再存入消息。

二、msgid和关键字

消息队列在内核中,要用一个非负整数来标记(类似于描述符或者句柄),这个非负整数称作msqid,即消息

队列的ID,但是要创建或者打开一个消息队列需要一个关键字,这个关键字其实是一个长整型数据。在进程

通信时,必须要约定使用同一个关键字,这样就可以得到同一个消息队列,因为消息队列是由内核维护的,

不同的进程使用相同关键字打开或者创建的消息队列,获得的msgid是相同的。

本例中使用一个文件路径加上课题ID(0~255)调用ftok函数产生一个关键字。

代码如下:

CMsgQueue.h

/*************************************************************************
> File Name: CMsgQueue.h
> Author: KentZhang
> Mail: zhchk2010@qq.com
> 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: zhchk2010@qq.com
> 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;
}


由于笔者的水平有限,出错在所难免,恳请读者拍砖指正,谢谢阅读
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: