C++封裝Linux訊息佇列
阿新 • • 發佈:2018-11-10
訊息佇列是Linux程序間通訊方式之一,在面向物件程式設計中,需要對其封裝。
一、訊息佇列的特點
1、非同步通訊,訊息佇列會儲存程序傳送的訊息,其他程序不一定要及時取走訊息。
2、可以傳送不同型別的訊息,訊息的頭部用long型別的欄位標記。
3、取訊息時,不一定按先進先出的方式,可以按訊息的型別來取。
4、訊息佇列內部是用一個連結串列來儲存訊息,當訊息被取走後,這個訊息就從連結串列中刪除了,而且這個連結串列的長度
是有限制的,當訊息存滿後,不能再存入訊息。
二、msgid和關鍵字
訊息佇列在核心中,要用一個非負整數來標記(類似於描述符或者控制代碼),這個非負整數稱作msqid,即訊息
佇列的ID,但是要建立或者開啟一個訊息佇列需要一個關鍵字,這個關鍵字其實是一個長整型資料。在程序
通訊時,必須要約定使用同一個關鍵字,這樣就可以得到同一個訊息佇列,因為訊息佇列是由核心維護的,
不同的程序使用相同關鍵字開啟或者建立的訊息佇列,獲得的msgid是相同的。
一、訊息佇列的特點
1、非同步通訊,訊息佇列會儲存程序傳送的訊息,其他程序不一定要及時取走訊息。
2、可以傳送不同型別的訊息,訊息的頭部用long型別的欄位標記。
3、取訊息時,不一定按先進先出的方式,可以按訊息的型別來取。
4、訊息佇列內部是用一個連結串列來儲存訊息,當訊息被取走後,這個訊息就從連結串列中刪除了,而且這個連結串列的長度
是有限制的,當訊息存滿後,不能再存入訊息。
二、msgid和關鍵字
訊息佇列在核心中,要用一個非負整數來標記(類似於描述符或者控制代碼),這個非負整數稱作msqid,即訊息
通訊時,必須要約定使用同一個關鍵字,這樣就可以得到同一個訊息佇列,因為訊息佇列是由核心維護的,
不同的程序使用相同關鍵字開啟或者建立的訊息佇列,獲得的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;
}
由於筆者的水平有限,出錯在所難免,懇請讀者拍磚指正,謝謝閱讀。