C++封装Linux消息队列

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/KentZhang_/article/details/48198427
消息队列是Linux进程间通信方式之一,在面向对象编程中,需要对其封装。

一、消息队列的特点
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;
}


由于笔者的水平有限,出错在所难免,恳请读者拍砖指正,谢谢阅读

猜你喜欢

转载自blog.csdn.net/KentZhang_/article/details/48198427