1. 程式人生 > >C++封裝Linux訊息佇列

C++封裝Linux訊息佇列

訊息佇列是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;
}


由於筆者的水平有限,出錯在所難免,懇請讀者拍磚指正,謝謝閱讀