基本的思想是,
1、将消息队列封装成一个类,可以简单地控制消息队列的建立、收发和删除。
2、更改消息协议时,不需要修改收发的类。
以下是代码,使用模板来达到上述第二点的要求,只要按规则定义了消息结构,则可以复用此消息队列的代码。因为使用了模板,所以使用了hpp头文件,将类和其成员函数都定义在hpp文件里,使用时只需要包含此头文件就可以了。
//DMMsgQueue.hpp
#ifndef _DMMSGQUEUE_HPP_
#define _DMMSGQUEUE_HPP_
#include <stdio.h>
#include <sys/msg.h>
#include <string.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/ipc.h>
#include <errno.h>
#include <stdlib.h>
#include <iostream>
#ifdef __LINUX__
const int SVMSG_MODE = 0660;
#else
#include <sys/ipc.h>
const int SVMSG_MODE = MSG_R | MSG_W;
#endif
const int FIND_OR_CREATE = SVMSG_MODE | IPC_CREAT;
const int CREATE = SVMSG_MODE | IPC_CREAT | IPC_EXCL;
/**
* @brief Structure of message sample, every structure of message must contain
* the member mtype and member funtion MsgSize just like this sample struct and
* you can add more members to fit your need
*/
struct DMBaseMsg
{
long mtype;
size_t MsgSize() const
{
return 0;
}
};
/**
* @brief Message queue base on system V message queue
*/
template <typename T>
class DMMsgQueue
{
public:
DMMsgQueue(std::string strQueueName, int flag = FIND_OR_CREATE);
~DMMsgQueue(){};
bool Recv(T& msgObject, int flag = 0);
bool Send(const T& msgObject, int flag = 0);
bool RecvTimeOut(T& msgObject, int nTimeout = 5);
bool SendTimeOut(const T& msgObject, int nTimeout = 5);
bool Remove();
private:
int m_Msqid;
};
template <typename T>
DMMsgQueue<T>::DMMsgQueue(std::string strQueueName, int flag)
{
std::string strFile= "/tmp/";
strFile += strQueueName;
std::string strCmd = "touch ";
strCmd += strFile;
system(strCmd.c_str());
key_t mqkey = ftok(strFile.c_str(), 42);
if(mqkey == -1)
{
throw TCException("DMMsgQueue create error!Ftok error!");
}
m_Msqid = msgget(mqkey, flag);
if(errno = EEXIST && flag == CREATE)
{
throw TCException("DMMsgQueue create error!MsgQueue Existed!");
}
if(m_Msqid == -1)
{
PRINTTRACE(g_szError, "[DMMsgQueue::DMMsgQueue]Error! errno:%d\n", errno);
throw TCException("DMMsgQueue create error!");
}
}
template <typename T>
bool DMMsgQueue<T>::Send(const T& msgObject, int flag)
{
if(0 == msgsnd(m_Msqid, (void*)&msgObject, msgObject.MsgSize(),flag))
{
return true;
}
else
{
return false;
}
}
template <typename T>
bool DMMsgQueue<T>::Recv(T& msgObject, int flag)
{
if(0 < msgrcv(m_Msqid, (void*)&msgObject, msgObject.MsgSize(),msgObject.mtype, flag))
{
return true;
}
else
{
return false;
}
}
template <typename T>
bool DMMsgQueue<T>::RecvTimeOut(T& msgObject, int nTimeout)
{
time_t starttime = time(NULL);
while(true)
{
int nReturn = 0;
nReturn = msgrcv(m_Msqid, (void*)&msgObject, msgObject.MsgSize(), msgObject.mtype, IPC_NOWAIT);
if(0 < nReturn)
{
return true;
}
else
{
if(errno == ENOMSG || 0 == nReturn)
{
if(time(NULL) - starttime < nTimeout)
{
usleep(50);
continue;
}
}
return false;
}
}
}
template <typename T>
bool DMMsgQueue<T>::SendTimeOut(const T& msgObject, int nTimeout)
{
time_t starttime = time(NULL);
while(true)
{
int nReturn = 0;
nReturn = msgsnd(m_Msqid, (void*)&msgObject, msgObject.MsgSize(), IPC_NOWAIT);
if(0 == nReturn)
{
return true;
}
else
{
if(errno == EAGAIN)
{
if(time(NULL) - starttime < nTimeout)
{
usleep(50);
continue;
}
}
return false;
}
}
}
/**
* @brief If you want to remove a message queue you have created , try this
*
* @return
*/
template <typename T>
bool DMMsgQueue<T>::Remove()
{
if(!msgctl(m_Msqid, IPC_RMID, NULL))
{
return false;
}
return true;
}
#endif
这里需要注意一点,编译时必须定义宏__LINUX__,这里主要出于我工作时移植性的考虑,某些宏在AIX上有定义,而linux中没有。另外,构造函数中所使用的ftok函数的注意事项,可以参照我之前写的另一个文章。
以下再举一个测试函数例子:
#include "DMMsgQueue.hpp"
int main(int argc, char **argv)
{
DMBaseMsg msg;
msg.mtype = 100;
memset(msg.mmsg, 0x00, 1024);
sprintf(msg.mmsg, "abc");
DMMsgQueue<DMBaseMsg> my_mq("my_mq");
std::cout << "Sending..." << std::endl;
if(!my_mq.Send(msg))
{
std::cout << "send error .." << std::endl;
my_mq.Remove();
return -1;
}
DMBaseMsg msg2;
msg2.mtype = 200;
std::cout << "Receiving..." << std::endl;
if(!my_mq.RecvTimeOut(msg2, 10))
{
std::cout << "Receive error" << std::endl;
my_mq.Remove();
return -1;
}
std::cout << msg2.mmsg<< std::endl;
my_mq.Remove();
return 0;
}