This is the C++/Qt implementation of the message bus program. The principles of the message bus are described in the main message bus article. Do read that article first.
Here I have used Qt’s abstractions for socket, so sorry, no down-and-dirty socket()/bind()/accept() code.
The source code
This source is split in two files, since Qt wants a header file to scan; putting the class declarations in the .cc file does not work. And anyway, it is good style, and more readable, to put declarations is a separate header file.
The Qt project file
# -*- mode: Makefile; coding: us-ascii-unix -*- QT += core QT += widgets QT += network HEADERS += redgreenblue.h SOURCES += redgreenblue.cc
Header file
// -*- coding: us-ascii-unix -*- # pragma once # include <qobject.h> # include <qwidget.h> class QTcpServer; class QTcpSocket; class QTextStream; class QPushButton; class Server : public QObject { Q_OBJECT public: Server(int port, QObject *parent = 0); ~Server(); void accept_connection(); private: QTcpServer *_tcp_server = 0; }; class Worker : public QObject { Q_OBJECT public: Worker(QTcpSocket *s, Server *parent = 0); ~Worker(); void write_current_colour(); private: void _read_from_client(); QTcpSocket *_tcp_socket = 0; }; class Client : public QObject { public: Client(int port); void send_message(char const *buf); static Client *instance; private: void _data_arrived(); void _connection_closed(); QTcpSocket *_tcp_socket = 0; }; class Gui : public QWidget { Q_OBJECT public: Gui(QWidget *parent = 0); void on_incoming_message(char const *message); static Gui *instance; private: QPushButton *btn(char const *text, int id, Gui *parent); void on_clicked(); QPushButton *red = 0; QPushButton *green = 0; QPushButton *blue = 0; };
Source file
// -*- coding: us-ascii-unix -*- # include <redgreenblue.h> # include <qapplication.h> //-- QApplication # include <qtcpserver.h> //-- QTcpServer # include <qtcpsocket.h> //-- QTcpSocket # include <qhostaddress.h> //-- QHostAddress # include <qevent.h> //-- QEvent # include <qlabel.h> //-- QLabel # include <qlayout.h> //-- QLayout # include <qpushbutton.h> //-- QPushButton # include <qbuttongroup.h> //-- QButtonGroup # include <qtextstream.h> //-- QTextSTream # include <qpalette.h> //-- QPalette # include <qcolor.h> //-- QColor # include <set> # include <assert.h> # include <stdlib.h> enum { RED = 1001, GREEN, BLUE }; class Server; class Worker; static struct { std::set<Worker *> clients; int current_colour = RED; } s; //---------------------------------------------------------------------------------------- Server::Server(int port, QObject *parent) : QObject(parent), _tcp_server(new QTcpServer(this)) { if (_tcp_server->listen(QHostAddress::Any, port)) assert(connect(_tcp_server, &QTcpServer::newConnection, this, &Server::accept_connection)); } Server::~Server() { } void Server::accept_connection() { printf("Incoming connection\n"); while (QTcpSocket *s = _tcp_server->nextPendingConnection()) { printf("Adding a pending connection\n"); (new Worker(s, this))->write_current_colour(); } } //---------------------------------------------------------------------------------------- Worker::Worker(QTcpSocket *tcp_socket, Server *parent) : QObject(parent), _tcp_socket(tcp_socket) { s.clients.insert(this); assert(connect(_tcp_socket, &QTcpSocket::readyRead, this, &Worker ::_read_from_client)); assert(connect(_tcp_socket, &QTcpSocket::disconnected, this, &Worker ::deleteLater )); assert(connect(_tcp_socket, &QTcpSocket::disconnected, _tcp_socket, &QTcpSocket::deleteLater )); } Worker::~Worker() { s.clients.erase(this); } void Worker::write_current_colour() { char buf[20]; snprintf(buf, sizeof buf, "%d\n", s.current_colour); assert(strlen(buf) == 0LLU + _tcp_socket->write(buf)); } void Worker::_read_from_client() { char buf[20]; auto count = _tcp_socket->QIODevice::readLine(buf, sizeof buf); assert(1 < count && count < 10); for (auto it : s.clients) assert(strlen(buf) == 0LLU + it->_tcp_socket->write(buf)); } //----------------------------------------------------------------------------- Client *Client::instance = 0; Client::Client(int port) : _tcp_socket(new QTcpSocket(this)) { assert(0 == instance); _tcp_socket->connectToHost("localhost", port); printf("Connection opened\n"); if (_tcp_socket->isOpen()) { assert(connect(_tcp_socket, &QTcpSocket::readyRead, this, &Client::_data_arrived )); //dataAvailable(int) assert(connect(_tcp_socket, &QTcpSocket::disconnected, this, &Client::_connection_closed)); } else { printf("******* Gui::open_connection(): Failed\n"); exit(1); } instance = this; } void Client::_data_arrived() { char buf[20]; auto n = _tcp_socket->QIODevice::readLine(buf, sizeof buf); assert(1 < n && n < 10); for (--n; n >= 0 && buf[n] <= ' '; n--) buf[n] = 0; Gui::instance->on_incoming_message(buf); } void Client::_connection_closed() { exit(0); } void Client::send_message(char const *buf) { printf("Writing to server: [%s]\n", buf); _tcp_socket->write(buf); } //----------------------------------------------------------------------------- Gui *Gui::instance = 0; static struct { QPalette p_white; QPalette p_red; QPalette p_green; QPalette p_blue; } s_gui; Gui::Gui(QWidget *parent) : QWidget(parent ), red (btn("Red", RED, this)), green (btn("Green", GREEN, this)), blue (btn("Blue", BLUE, this)) { assert(0 == instance); QBoxLayout *buttons = new QHBoxLayout(); buttons->addWidget(red ); buttons->addWidget(green); buttons->addWidget(blue ); s_gui.p_white = red->palette(); s_gui.p_red = s_gui.p_white; s_gui.p_green = s_gui.p_white; s_gui.p_blue = s_gui.p_white; s_gui.p_red .setColor(QPalette::Button, QColor(Qt::red )); s_gui.p_green.setColor(QPalette::Button, QColor(Qt::green)); s_gui.p_blue .setColor(QPalette::Button, QColor(Qt::blue )); this->setWindowTitle("C++ and Qt"); this->setLayout(buttons); instance = this; } QPushButton *Gui::btn(char const *text, int id, Gui *parent) { QPushButton *rval = new QPushButton(text, parent); rval ->setMinimumSize(rval ->sizeHint()); rval ->setProperty ("id", id); rval ->setMinimumSize(100, 30); assert(QObject::connect(rval, &QPushButton::clicked, parent, &Gui::on_clicked)); return rval; } void Gui::on_clicked () { char buf[20]; auto var = sender()->property("id"); int id = var.toInt(); snprintf(buf, sizeof buf, "%d\n", id); Client::instance->send_message(buf); } void Gui::on_incoming_message(char const *message) { int colour = atoi(message); red ->setPalette(RED == colour ? s_gui.p_red : s_gui.p_white); red ->update(); green->setPalette(GREEN == colour ? s_gui.p_green : s_gui.p_white); green->update(); blue ->setPalette(BLUE == colour ? s_gui.p_blue : s_gui.p_white); blue ->update(); } int main(int argc, char **argv) { QApplication app(argc, argv); Server server(4711); Client client(4711); Gui *f = new Gui(); f->show(); app.exec(); return 0; }
You can reach me by email at “lars dash 7 dot sdu dot se” or by telephone +46 705 189090