|
|
| Line 5: |
Line 5: |
| === The Logic === | | === The Logic === |
| This application will use a central server that will manage the communication among clients via [https://www.json.org/ JSON] messages. | | This application will use a central server that will manage the communication among clients via [https://www.json.org/ JSON] messages. |
| On the server side, we'll distribute the clients over multiple threads to speed up the processing.
| | We'll implement 2 versions of the server, one that runs in a single server and one that distributes the sockets among multiple threads. |
| === The Server Socket ===
| |
| The fist class we'll look at is the socket on the server that will communicate with a single client
| |
| <code>
| |
| #include <QTcpSocket>
| |
| class ServerSocket : public QTcpSocket
| |
| {
| |
| Q_OBJECT
| |
| Q_DISABLE_COPY(ServerSocket)
| |
| public:
| |
| explicit ServerSocket(QObject* parent = nullptr);
| |
| private slots:
| |
| void receiveJson();
| |
| void sendJson(const QByteArray& jsonData);
| |
| signals:
| |
| void jsonReceived(const QJsonDocument& jsonDoc);
| |
| };
| |
| </code>
| |
| The declaration is very simple, we are just adding 2 slots: <tt>sendJson</tt> to send messages to the client and <tt>receiveJson</tt> to receive and decode a message coming from the client.
| |
| The <tt>jsonReceived</tt> signal will notify the server of incoming data.
| |
| <code>
| |
| #include "serversocket.h"
| |
| #include <QDataStream>
| |
| #include <QJsonDocument>
| |
| #include <QJsonParseError>
| |
| ServerSocket::ServerSocket(QObject* parent)
| |
| :QTcpSocket(parent)
| |
| {
| |
| connect(this,&ServerSocket::readyRead,this,&ServerSocket::receiveJson);
| |
| }
| |
| | |
| void ServerSocket::sendJson(const QByteArray &jsonData)
| |
| {
| |
| QDataStream socketStream(this);
| |
| socketStream.setVersion(QDataStream::Qt_5_6);
| |
| socketStream << jsonData;
| |
| }
| |
| | |
| void ServerSocket::receiveJson()
| |
| {
| |
| QByteArray jsonData;
| |
| QDataStream socketStream(this);
| |
| socketStream.setVersion(QDataStream::Qt_5_6);
| |
| for(;;){
| |
| socketStream.startTransaction();
| |
| socketStream >> jsonData;
| |
| if(socketStream.commitTransaction()){
| |
| QJsonParseError parseError;
| |
| const QJsonDocument jsonDoc = QJsonDocument::fromJson(jsonData,&parseError);
| |
| if(parseError.error == QJsonParseError::NoError)
| |
| emit jsonReceived(jsonDoc);
| |
| }
| |
| else{
| |
| break;
| |
| }
| |
| }
| |
| }
| |
| </code>
| |
| The implementation is also relatively straightforward.
| |
| The constructor calls the base class and the connects the <tt>readyRead</tt> signal to <tt>receiveJson</tt> slot that will take care of decoding the data.
| |
| <tt>sendJson</tt> will just write the data to the socket. <tt>socketStream.setVersion(QDataStream::Qt_5_6);</tt> makes sure that clients compiled with different versions of Qt all communicate in the same way.
| |
| <tt>receiveJson</tt> is just slightly more involved: since <tt>readyRead</tt> is emitted when there is '''some''' data available to read but not necessarily '''all''' of it, we start a transaction.
| |
| We then start an infinite loop that keeps trying to read JSON data. If the data was read correctly, <tt>commitTransaction</tt> will return true and we proceed with parsing the JSON into a <tt>QJsonDocument</tt> otherwise we just stop and wait for more data to arrive.
| |
Introduction
This article will illustrate a simple chat client and server communicating over TCP.
The aim is to clarify aspects of QTcpSocket/QTcpServer that are not developed in the official Qt Fortune example.
This has no intention to be a fully featured chat application
The Logic
This application will use a central server that will manage the communication among clients via JSON messages.
We'll implement 2 versions of the server, one that runs in a single server and one that distributes the sockets among multiple threads.