Linux c/c++入门基础-实现简单的消息队列类

简介

看完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$ 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

青山亦如是

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值