简介
本文介绍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文件夹下会有消息队列文件;