Qt使用ZMQ进行多线程发布订阅

知识点涉及:1, Qt多线程编程

2, ZMQ使用发布订阅模式通信

使用python 写zmq 发布订阅, https://www.cnblogs.com/yezl/p/6604680.html点击打开链接

.h

#ifndef ZMQMANAGER_H
#define ZMQMANAGER_H

#include <QObject>
#include "zmq.h"

/************************************************************
*description : 通信管理类,应用zmq 发布订阅模式,zmq 版本:zmq 4.0.4, vs2015
*author : gyh
*date : 2018/5/4 11:24 
*last change : 
*************************************************************/

class Publisher;
class SubScriber;
class ZMQManager : public QObject
{
	Q_OBJECT

public:
	ZMQManager(QObject *parent = nullptr);
	~ZMQManager();

	void sendData(const QByteArray &ba);

signals:
	void sigSendData(const QByteArray &ba);
	void sigRun();

public slots:
	void slotRecv(int cmd, const QString &status);

private:
	Publisher * m_pub;
	SubScriber * m_sub;
};

/************************************************************
*description : 发布端
*author : gyh
*date : 2018/5/3 17:02 
*last change : 
*************************************************************/
class Publisher : public QObject
{
	Q_OBJECT
public:
	Publisher(QObject * parent = nullptr);
	~Publisher();

	void initZMQ();
	// 发送数据
	void sendData(const QByteArray &ba);

private:
	void * m_context;
	void * m_publisher; //发布者
};

/************************************************************
*description : 订阅端
*author : gyh
*date : 2018/5/3 17:02 
*last change : 
*************************************************************/
class SubScriber : public QObject
{
	Q_OBJECT
public:
	SubScriber(QObject * parent = nullptr);
	~SubScriber();

	void initZMQ();
	void recv(); //接收

signals:
	void sigRecv(int cmd, const QString &status);

private:
	void * m_context;
	void * m_subscriber; //订阅者
};

#endif // ZMQMANAGER_H

.cpp

#include "zmqmanager.h"
#include "share/lkQDebug.h"
#include <QDebug>
#include <assert.h>
#include <QThread>

ZMQManager::ZMQManager(QObject *parent)
	: QObject(parent)
{
	QThread * pubThread = new QThread;
	m_pub = new Publisher;
	m_pub->moveToThread(pubThread);
	pubThread->start();
	connect(this, &ZMQManager::sigSendData, m_pub, &Publisher::sendData);

	QThread * subThread = new QThread;
	m_sub = new SubScriber;
	m_sub->moveToThread(subThread);
	subThread->start();
	connect(m_sub, &SubScriber::sigRecv, this, &ZMQManager::slotRecv);

	connect(this, &ZMQManager::sigRun, m_sub, &SubScriber::recv);
}

ZMQManager::~ZMQManager()
{

}

void ZMQManager::sendData(const QByteArray &ba)
{
	DBG_LOG(11, QStringLiteral("主线程%1 发送数据").arg((int)QThread::currentThreadId()));
	sigSendData(ba);
}

void ZMQManager::slotRecv(int cmd, const QString &status)
{
	DBG_LOG(11, QStringLiteral("主线程%1 收到数据 %2 %3").arg((int)QThread::currentThreadId()).arg(cmd).arg(status));
}

//
Publisher::Publisher(QObject * parent /*= nullptr*/)
	:QObject(parent)
{
	initZMQ();
}

Publisher::~Publisher()
{
	zmq_close(m_publisher); //关闭发布者
	zmq_ctx_destroy(m_context); //释放发布者上下文
}

void Publisher::initZMQ()
{
	m_context = zmq_ctx_new(); //创建zmq上下文
	m_publisher = zmq_socket(m_context, ZMQ_PUB); //创建发布端套接字,非线程安全
	int rc = zmq_bind(m_publisher, "tcp://*:6666"); //绑定端口6666
	assert(rc == 0);
}

void Publisher::sendData(const QByteArray &ba)
{
	int ret = zmq_send(m_publisher, ba.data(), ba.size(), 0); //发布固定内存数据
	DBG_LOG(11, QStringLiteral("发布线程 %1, 发送数据 %2 字节").arg((int)QThread::currentThreadId()).arg(ret));
}

//
SubScriber::SubScriber(QObject * parent /*= nullptr*/)
	: QObject(parent)
{
	initZMQ();
}

SubScriber::~SubScriber()
{
	zmq_close(m_subscriber); //关闭订阅端
	zmq_ctx_destroy(m_context);  //释放订阅端上下文
}

void SubScriber::initZMQ()
{
	m_context = zmq_ctx_new(); //创建订阅端上下文
	m_subscriber = zmq_socket(m_context, ZMQ_SUB); //创建订阅端套接字
	int rc = zmq_connect(m_subscriber, "tcp://localhost:6666"); //连接端口6666
	assert(rc == 0);
	if (rc == -1)
	{
		int err = zmq_errno();
		DBG_LOG(11, QString("connect error %1").arg(err));
		return;
	}

	/*zmq_setsockopt()函数会对socket参数指定的socket进行设置,
	设置的属性由option_name参数指定,属性值由参数option_value指定。
	option_len参数指定属性值的数据存储空间的大小
	option: 查看函数翻译文档 https://www.cnblogs.com/fengbohello/p/4398953.html
	*/
	int ret = zmq_setsockopt(m_subscriber, ZMQ_SUBSCRIBE, NULL, 0);
}

void SubScriber::recv()
{
	int size = 0;
	int cmdCode;
	char buffer[256];
	int nbytes = 0;
	int offset = 0;
	forever
	{
		// zmq_recv (void *s, void *buf, size_t len, int flags)
		// zmq_recv()函数会从socket参数指定的socket上接收一个消息,
		//并把这个消息存储在buf参数指定的内存空间中。超过len参数指
		//定长度的任何数据都会被删去。如果在socket上没有消息可接收,
		//zmq_recv()函数会进行阻塞,直到请求被满足为止。flag参数是由下面的选项组合而成的标志。
		//ZMQ_DONTWAIT:此函数在非阻塞模式下执行
		//Multi-part:一个ZMQ消息由1个或多个ZMQ消息帧组成
		memset(buffer, 0, 256 * sizeof(char));
		nbytes = zmq_recv(m_subscriber, &buffer, 256, 0);
		if (nbytes == -1) {
			qDebug() << "receiver cmd null , continue";;
			continue;
		}
		DBG_LOG(11, QStringLiteral("订阅线程%1 , 收到数据 %2字节").arg((int)QThread::currentThreadId()).arg(nbytes));
		memcpy(&cmdCode, buffer + offset, 4); offset += 4;
		memcpy(&size, buffer + offset, 4); offset += 4;
		switch (cmdCode)
		{
		case 1001:
			{
				QString state(buffer+offset);
				DBG_LOG(11, QStringLiteral("收到命令%1, 状态%2").arg(cmdCode).arg(state));
				sigRecv(cmdCode, state);
			}
			break;
		default:
			break;
		}
	}
}

main

#include "zmqmanager.h"
#include <QtWidgets/QApplication>
#include <QSettings>
#include <QTimer>

int main(int argc, char *argv[])
{
	QApplication a(argc, argv);
	a.setOrganizationDomain("WYX");
	a.setApplicationName("ExamTeacher");

	//服务器IP
 	QSettings confSetting(DIR_CONFIG + "conf_teacher.ini", QSettings::IniFormat);
 	QString address = confSetting.value("SERVER/IP").toString();
 	int port = confSetting.value("SERVER/Port", 6379).toInt();

#ifdef __TEST__
	ZMQManager zmqManager;
	zmqManager.sigRun();
	QTimer::singleShot(1000, &zmqManager, [&]() {
		int cmd = 1001;
		int size = 128;
		const char * state = "connected";
		QByteArray ba;
		ba.append((char*)&cmd, sizeof(cmd));
		ba.append((char*)&size, sizeof(size));
		ba.append(state, 128);
		int n = ba.size();
		zmqManager.sendData(ba);
	});
#endif

#ifdef __TEST__
	//w.startRedis();
	//w.show();
//#else
	ExamLogin * login = new ExamLogin(&w);
	login->exec();
#endif
	return a.exec();
}


  • 5
    点赞
  • 32
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值