WIP-How to create a simple chat application: Difference between revisions

From Qt Wiki
Jump to navigation Jump to search
No edit summary
No edit summary
Line 39: Line 39:
{
{
     QDataStream socketStream(this);
     QDataStream socketStream(this);
     socketStream.setVersion(QDataStream::Qt_5_9);
     socketStream.setVersion(QDataStream::Qt_5_6);
     socketStream << jsonData;
     socketStream << jsonData;
}
}
Line 47: Line 47:
     QByteArray jsonData;
     QByteArray jsonData;
     QDataStream socketStream(this);
     QDataStream socketStream(this);
     socketStream.setVersion(QDataStream::Qt_5_9);
     socketStream.setVersion(QDataStream::Qt_5_6);
     socketStream.startTransaction();
     for(;;){
    socketStream >> jsonData;
        socketStream.startTransaction();
    if(socketStream.commitTransaction()){
        socketStream >> jsonData;
        QJsonParseError parseError;
        if(socketStream.commitTransaction()){
        const QJsonDocument jsonDoc = QJsonDocument::fromJson(jsonData,&parseError);
            QJsonParseError parseError;
        if(parseError.error == QJsonParseError::NoError)
            const QJsonDocument jsonDoc = QJsonDocument::fromJson(jsonData,&parseError);
            emit jsonReceived(jsonDoc);
            if(parseError.error == QJsonParseError::NoError)
                emit jsonReceived(jsonDoc);
        }
        else{
            break;
        }
     }
     }
}
}
Line 60: Line 65:
The implementation is also relatively straightforward.
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.
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>
<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.

Revision as of 15:40, 15 May 2018

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. On the server side, we'll distribute the clients over multiple threads to speed up the processing.

The Server Socket

The fist class we'll look at is the socket on the server that will communicate with a single client

#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);
};

The declaration is very simple, we are just adding 2 slots: sendJson to send messages to the client and receiveJson to receive and decode a message coming from the client. The jsonReceived signal will notify the server of incoming data.

#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;
        }
    }
}

The implementation is also relatively straightforward. The constructor calls the base class and the connects the readyRead signal to receiveJson slot that will take care of decoding the data. sendJson will just write the data to the socket. socketStream.setVersion(QDataStream::Qt_5_6); makes sure that clients compiled with different versions of Qt all communicate in the same way. receiveJson is just slightly more involved: since readyRead 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, commitTransaction will return true and we proceed with parsing the JSON into a QJsonDocument otherwise we just stop and wait for more data to arrive.