简介
看完linux系统编程的消息队列后,决定使用c++封装一个简单的消息队列基类,实现简单的初始化功能,预留消息接收部分(虚函数)。消息的具体解析,由基类的子类去具体实现解析业务。此次尝试消息队列实现进程间通信。不知道消息队列的小伙伴们,可以去豆包、百度等搜索 Linux 消息队列的介绍,也不是很复杂,基本都是初始化、获取key、发送和接收等。
废话少说,直接贴代码
基类实现
BaseQueue.h
//消息队列例子
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
#include <log4z.h>
#include <thread>
#include <unistd.h>
using namespace zsummer::log4z;
using namespace std;
#define MSGMAX 2048
#pragma pack(1)
struct s_msgbuf
{
long mtype;
char mtext[MSGMAX];
};
#pragma pack()
class BaseQueue
{
public:
BaseQueue();
BaseQueue(char* path, int projID, int msgtype);
~BaseQueue();
int init();
int create_msgqueue(key_t &key);
int start_receive();
int start_send();
int send(char *send_data, int len);
void deleteThread(std::thread*& t);
void quit();
void stop();
void printKey();
public:
bool b_send_enable;
bool b_receive_enable;
public:
void recv_loop();
virtual bool _vReadData(char* input)=0;
char *path_name = nullptr;
int proj_id;
private:
int msgid;
key_t key;
int msg_type = 0x34;
struct s_msgbuf send_msg;
struct s_msgbuf recv_msg;
bool _quit;
std::thread* _thread;
};
说明:
1、日志打印采用log4z
2、recv_loop()针对接收部分开启的线程,其中虚函数_vReadData部分最终由子类去实现具体的业务解析。后面的子类有介绍。
3、send_msg 和 recv_msg 分别属于发送和接收部分。
4、使用发送功能,就调用start_send接口;使用接收功能,就使用start_receive()接口
接下来是基类BaseQueue.cpp的实现
BaseQueue.cpp
#include "BaseQueue.h"
BaseQueue::BaseQueue()
{
_quit = false;
_thread = nullptr;
key = -1;
msgid = -1;
}
BaseQueue::BaseQueue(char* path, int projID, int msgtype)
{
if ((path == NULL) || (projID < 0) || (msgtype <= 0))
{
LOGFMTE("msg: struct function parameter error");
return;
}
path_name = path;
proj_id = projID;
msg_type = msgtype;
_quit = false;
_thread = nullptr;
key = -1;
msgid = -1;
}
BaseQueue::~BaseQueue()
{
if (path_name)
{
path_name = nullptr;
}
stop();
LOGFMTI("BaseQueue: base deinit structor.");
}
int BaseQueue::init()
{
if (path_name)
{
key = ftok(path_name, proj_id);
}
else
{
key = ftok(".", 'a');
}
if (key < 0)
{
LOGFMTE("BaseQueue: ftok error.");
}
return key;
}
int BaseQueue::create_msgqueue(key_t &key)
{
if (key >= 0)
{
msgid = msgget(key,0777 | IPC_CREAT);
if (msgid < 0)
{
LOGFMTE("BaseQueue: msgget error.");
}
return msgid;
}
else
{
LOGFMTE("BaseQueue: key error.");
return -1;
}
}
int BaseQueue::start_receive()
{
b_receive_enable = false;
if (init() != -1)
{
if (create_msgqueue(key) != -1)
{
_quit = false;
_thread = new std::thread(&BaseQueue::recv_loop, this);
b_receive_enable = true;
return 0;
}
else
{
return -2;
}
}
else
{
return -3;
}
}
int BaseQueue::start_send()
{
b_send_enable = false;
if (init() != -1)
{
if (create_msgqueue(key) != -1)
{
b_send_enable = true;
return 0;
}
else
{
return -2;
}
}
else
{
return -3;
}
}
int BaseQueue::send(char *send_data, int len)
{
if ((send_data == NULL) || (len <= 0))
{
LOGFMTE("BaseQueue: send parameter error.");
return -1;
}
if (len>=MSGMAX)
{
len = MSGMAX-1;
}
memset(send_msg.mtext, 0x0, sizeof(send_msg.mtext));
send_msg.mtype = msg_type;
memcpy(send_msg.mtext, send_data, len);
if (msgsnd(msgid, &send_msg, len, 0) == -1)
{
LOGFMTE("BaseQueue: send msg error.");
return -1;
}
return 0;
}
void BaseQueue::deleteThread(std::thread *&t)
{
if (t)
{
if (t->joinable())
{
t->join();
}
delete t;
t = NULL;
}
}
void BaseQueue::quit()
{
_quit = true;
}
void BaseQueue::stop()
{
_quit = true;
deleteThread(_thread);
if (msgctl(msgid, IPC_RMID, NULL) == -1)
{
LOGFMTE("BaseQueue: delete msg error.");
}
LOGFMTI("BaseQueue: stop.");
}
void BaseQueue::printKey()
{
LOGFMTI("BaseQueue: msgID=%d,key=%d", msgid, key);
}
void BaseQueue::recv_loop()
{
LOGFMTI("BaseQueue: start msgqueue recv loop");
while(!_quit)
{
memset((uint8_t *)&recv_msg, 0x0, sizeof(recv_msg));
if (msgrcv(msgid, (void*)&recv_msg, MSGMAX, msg_type, IPC_NOWAIT) == -1)
{
//LOGFMTE("msg: receive error.\n");
}
else
{
_vReadData((char* )&recv_msg.mtext[0]);
}
usleep(20000);
}
LOGFMTI("BaseQueue: end msg receive...\n");
}
注意:在recv_loop中,_vReadData接口由子类去实现。
接下来看子类HelloMsgQueue具体的实现.
子类实现
#include "BaseQueue.h"
using namespace std;
class HelloMsgQueue : public BaseQueue
{
public:
HelloMsgQueue(char* path, int projID, int msgtype) : BaseQueue(path, projID, msgtype)
{
}
~HelloMsgQueue()
{
}
bool _vReadData(char * input)
{
bool ret = true;
if (input != NULL)
{
LOGFMTI("HelloMsgQueue: recv %s .", input);
}
return ret;
}
};
如上代码所示,重写_vReadData()接口,实现具体的业务。
Demo使用
使用过程如下:
int main(int argc, char *argv[])
{
log_init();
usleep(800000);
uint8_t iCount = 10;
HelloMsgQueue Hello_send(".", 'a', 0x56);
HelloMsgQueue Hello_recv(".", 'a', 0x56);
Hello_send.start_send();
Hello_send.start_receive();
while(iCount)
{
Hello_send.send("Hello msg queue...", strlen("Hello msg queue..."));
usleep(1000000);
iCount--;
}
LOGFMTI("Test end.");
return 0;
}
编译、运行如下所示:
2025-08-10 16:09:53.262 [9870] LOG_ALARM ----------------- log4z thread started! ---------------------------- log4z.cpp:1923
2025-08-10 16:09:53.262 [9870] LOG_ALARM logger id=0 key=Main name=logTest path=./log/ level=1 display=true log4z.cpp:1933
2025-08-10 16:09:54.113 [9872] LOG_INFO BaseQueue: start msgqueue recv loop BaseQueue.cpp:187
2025-08-10 16:09:54.113 [9872] LOG_INFO HelloMsgQueue: recv Hello msg queue... . main.cpp:52
2025-08-10 16:09:55.129 [9872] LOG_INFO HelloMsgQueue: recv Hello msg queue... . main.cpp:52
2025-08-10 16:09:56.121 [9872] LOG_INFO HelloMsgQueue: recv Hello msg queue... . main.cpp:52
2025-08-10 16:09:57.117 [9872] LOG_INFO HelloMsgQueue: recv Hello msg queue... . main.cpp:52
2025-08-10 16:09:58.114 [9872] LOG_INFO HelloMsgQueue: recv Hello msg queue... . main.cpp:52
2025-08-10 16:09:59.115 [9872] LOG_INFO HelloMsgQueue: recv Hello msg queue... . main.cpp:52
2025-08-10 16:10:00.136 [9872] LOG_INFO HelloMsgQueue: recv Hello msg queue... . main.cpp:52
2025-08-10 16:10:01.128 [9872] LOG_INFO HelloMsgQueue: recv Hello msg queue... . main.cpp:52
2025-08-10 16:10:02.118 [9872] LOG_INFO HelloMsgQueue: recv Hello msg queue... . main.cpp:52
2025-08-10 16:10:03.116 [9872] LOG_INFO HelloMsgQueue: recv Hello msg queue... . main.cpp:52
log4z stopping
2025-08-10 16:10:04.116 [9869] LOG_INFO Test end. main.cpp:97
2025-08-10 16:10:04.116 [9869] LOG_ERROR BaseQueue: delete msg error. BaseQueue.cpp:174
2025-08-10 16:10:04.116 [9869] LOG_INFO BaseQueue: stop. BaseQueue.cpp:177
2025-08-10 16:10:04.116 [9869] LOG_INFO BaseQueue: base deinit structor. BaseQueue.cpp:38
2025-08-10 16:10:04.134 [9872] LOG_INFO BaseQueue: end msg receive...
BaseQueue.cpp:203
2025-08-10 16:10:04.135 [9869] LOG_INFO BaseQueue: stop. BaseQueue.cpp:177
2025-08-10 16:10:04.135 [9869] LOG_INFO BaseQueue: base deinit structor. BaseQueue.cpp:38
dilake@ubuntu:~/home/dilake/msg_demo/desktop-build$
646

被折叠的 条评论
为什么被折叠?



