Qt实现IPC进程间通信-mqueue消息队列

简介

本文介绍QT在linux下使用mqueue消息队列实现进程间通信;
需包含头文件#include <mqueue.h>;
pro文件添加编译选项:LIBS += -lrt;
消息队列特征:可以设置最大消息个数、每个消息最大字节数,可以往消息队列写入多条消息,other进程读取一条,消息队列就删除一条。

接口介绍

1、消息队列结构体mq_attr

struct mq_attr {
    
     
    long mq_flags;    // 0或者O_NONBLOCK 
    long mq_maxmsg; //队列中包含的消息数的最大限制数  
    long mq_msgsize; //每个消息大小的最大限制数
    long mq_curmsgs;  //当前队列中的消息数 
} 

2、获取消息队列的属性
一个进程在发送和接收消息之前,需要了解消息对象的属性,如消息的最大长度。以便设定接收和发送的buffer大小。

mqd_t mq_getattr(mqd_t mqdes, struct mq_attr *attr);

Mqdes:打开消息队列时获取的描述符。
Attr:指向结构struct mq_attr的指针,用来获取消息队列的四个属性。

3、设置消息队列属性
我们可以设置消息队列的属性,实际只能设置flag标志,说明队列中没有消息时,接收消息的进程是否在队列上继续等待。

mqd_t mq_setattr(mqd_t mqdes, struct mq_attr *newattr, struct mq_attr *oldattr); 

Mqdes:打开消息队列时获取的描述符。
Attr:指向结构struct mq_attr的指针,用来获取消息队列的最大消息个数和最大消息长度。放到数据结构的mq_maxmsg和mq_msgsize中。

4、发送消息
进程在打开消息队列后,可以使用下面的函数发送消息

int mq_send(mqd_t mqdes, const char *ptr, size_t len, unsigned int prio); 

mqdes: 打开消息队列时获得的描述符。
ptr: 指向发送缓冲区的指针,发送缓冲区存放了要发送的数据。
Len: 要发送的数据的长度。
prio :消息的优先级;它是一个小于 MQ_PRIO_MAX 的数,数值越大,优先级越高。
POSIX 消息队列在调用 mq_receive 时总是返回队列中 最高优先级的最早消息 。如果消息不需要设定优先级,那么可以在 mq_send 是置 prio 为 0 , mq_receive 的 prio 置为 NULL 。 返回值:发送成功,返回0,失败,返回-1.

5、接收消息
进程在打开消息队列后,可以使用下面的函数接收消息。

//如果mq_flags设置了O_NONBLOCK参数,则读取失败后,会立即返回-1,不会阻塞
ssize_t mq_receive(mqd_t mqdes, char *ptr, size_t len, unsigned int *prio); 

mqdes: 打开消息队列时获得的描述符。
ptr: 指向接收缓冲区的指针。接收缓冲区用来存放收到的消息。
Len: 接收缓冲区的长度。 len不能小于mq_msgsize,否则会返回EMSGSIZE
prio :消息的优先级;它是一个小于 MQ_PRIO_MAX 的数,数值越大,优先级越高。 POSIX 消息队列在调用 mq_receive 时总是返回队列中 最高优先级的最早消息 。如果消息不需要设定优先级,那么可以在 mq_send 是置 prio 为 0 , mq_receive 的 prio 置为 NULL 。 返回值: 接收成功,返回0,失败,返回-1.

6、消息队列关闭

mqd_t mq_close(mqd_t mqdes);//关闭消息队列,但不能删除它 成功返回0,失败返回-1   

7、删除消息队列

mqd_t mq_unlink(const char *name); //成功返回0,失败返回-1 

当某个进程还没有关闭此消息队列时,调用mq_unlink时,不会马上删除队列,当最后一个进程关闭队列时,该队列被删除

代码实现

示例新建两个工程,启动两个应用程序,使用消息队列进行通信,接口函数原型介绍见注释:
工程A
widget_pa.h

#ifndef WIDGET_PA_H
#define WIDGET_PA_H

#include <QWidget>
/**
  *s_irusr     允许文件的所有者读取它。
  *s_iwusr     允许文件的所有者写入它。
  *s_irgrp     允许文件的组读取它。
  *s_iwgrp     允许文件的组写入它。
  */
#define FILE_MODE (S_IWUSR|S_IRUSR|S_IRGRP|S_IROTH)
namespace Ui {
    
    
class Widget_pa;
}
class Widget_pa : public QWidget
{
    
    
    Q_OBJECT
public:
    explicit Widget_pa(QWidget *parent = 0);
    ~Widget_pa();
private slots:
    void on_pushButton_clicked();
private:
    Ui::Widget_pa *ui;
signals:
};

#endif

widget_pa.cpp

#include "widget_pa.h"
#include "ui_widget_pa.h"

#include <pthread.h>
#include <stdlib.h>
#include <unistd.h>
#include <stdio.h>
#include <mqueue.h>
#include <QThread>
#include <QTextCodec>
#include <QDebug>

Widget_pa::Widget_pa(QWidget *parent) :
    QWidget(parent),
    ui(new Ui::Widget_pa)
{
    
    
    ui->setupUi(this);
}

Widget_pa::~Widget_pa()
{
    
    
    //4、删除消息队列文件
    mq_unlink("/mq_test");
    delete ui;
}

//发送数据
void Widget_pa::on_pushButton_clicked()
{
    
    
    char buf[128];
    memset(buf,0,sizeof(buf));

    /**
     * @brief mq_attr消息队列结构体
     * long mq_flags;    // 0或者O_NONBLOCK
     * long mq_maxmsg; //队列中包含的消息数的最大限制数
     * long mq_msgsize; //每个消息大小的最大限制数
     * long mq_curmsgs;  //当前队列中的消息数
     */
    struct mq_attr attr;
    attr.mq_maxmsg = 10;		//最大消息10个
    attr.mq_msgsize = 128;	//每个消息限制大小128字节
    attr.mq_flags = 0;

    /**
     * @brief 1、创建一个新的消息队列或打开一个已存在的消息的队列
     * mqd_t:返回值为int型,消息队列序号
     * "/mq_test":消息队列名称
     * O_CREAT|O_RDWR:文件属性,类似open文件的选项,O_CREAT(若此文件不存在则创建它),O_RDWR(读、写打开)
     * FILE_MODE:文件权限
     * &attr:mq_attr消息队列结构体
     */
    mqd_t mqd = mq_open("/mq_test", O_CREAT|O_RDWR, FILE_MODE, &attr);
    if(-1 == mqd)
    {
    
    
        perror("mq_open error");
        return;
    }

    memcpy(buf,ui->textEdit->toPlainText().toLocal8Bit().data(),sizeof(buf));
    //2、发送消息
    int ret = mq_send(mqd, buf, strlen(buf), 2);
    if(ret == -1)
    {
    
    
        perror("mq_send error");
    }
    //3、关闭消息队列文件描述,但不删除,mqd参数:消息队列序号
    mq_close(mqd);
}

工程B
widget_pb.h

#ifndef WIDGET_PB_H
#define WIDGET_PB_H

#include <QWidget>
#include <QThread>

#define FILE_MODE (S_IWUSR|S_IRUSR|S_IRGRP|S_IROTH)
namespace Ui {
    
    
class Widget_pb;
}

class Widget_pb : public QWidget
{
    
    
    Q_OBJECT

public:
    explicit Widget_pb(QWidget *parent = 0);
    ~Widget_pb();

private slots:
    void on_pushButton_clicked();
private:
    Ui::Widget_pb *ui;
};

#endif // WIDGET_PB_H

widget_pb.cpp

#include "widget_pb.h"
#include "ui_widget_pb.h"
#include <pthread.h>
#include <stdlib.h>
#include <unistd.h>
#include <stdio.h>
#include <mqueue.h>

Widget_pb::Widget_pb(QWidget *parent) :
    QWidget(parent),
    ui(new Ui::Widget_pb)
{
    
    
    ui->setupUi(this);
}

Widget_pb::~Widget_pb()
{
    
    
    delete ui;
}

void Widget_pb::on_pushButton_clicked()
{
    
    
    int ret;
    char buf[128];
    memset(buf,0,sizeof(buf));
    unsigned int prio=0;

    struct mq_attr attr;
    attr.mq_maxmsg = 10;
    attr.mq_msgsize = 128;
    attr.mq_flags = 0;
    //1、创建或打开消息队列
    mqd_t mqd = mq_open("/mq_test", O_CREAT|O_RDWR|O_NONBLOCK, FILE_MODE, &attr);
    if(-1 == mqd)
    {
    
    
        perror("mq_open error");
        return;
    }

    //2、接收消息
    ret = mq_receive(mqd, buf, sizeof(buf), &prio);
    if (ret !=-1)
    {
    
    
        QString str = QString("ret=%1,read:%2").arg(QString::number(ret)).arg(buf);
        ui->textEdit->append(str);
    }
    //3、关闭消息队列
    mq_close(mqd);
}

效果展示

在这里插入图片描述

消息队列文件

创建消息队列后,在/dev/mqueue文件夹下会有消息队列文件;
在这里插入图片描述

猜你喜欢

转载自blog.csdn.net/weixin_40355471/article/details/113178838