Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
162 views
in Technique[技术] by (71.8m points)

c++ - Qt, Sending multiple data types from client to server + data Streaming

I have a Client/Server based Qt application, using QTcpServer and QTcpSocket, I managed to do the connection and send some data back and forth between the client and the server. The client sends many types of data to the server (string, int, files and a real time audio stream) and since my server impliment a single data input SLOT (readyRead()):

connect(socket, SIGNAL(readyRead()),this, SLOT(readyRead()));

I don't know how could I distinguish between all this received data and call respectively the right function in the server.

Example (in the server):
- if I receive string        => call function showData(QString data);
- if I receive file          => call function saveFile(QFile file);
- if I receive audio stream  => play audio stream
- ...

SERVER:

void Server::newClientConnection()
{
    QTcpSocket *socket = server->nextPendingConnection();

    connect(socket, SIGNAL(readyRead()), this, SLOT(readyRead()));
    //...
}

void Server::readyRead()
{
    QTcpSocket *clientSocket = qobject_cast<QTcpSocket *>(sender());
    if (clientSocket == 0) {
        return;
    }

    QDataStream in(clientSocket);

    if (sizeMessageClient == 0)
    {
        if (clientSocket->bytesAvailable() < (int)sizeof(quint16)){
             return;
        }
        in >> sizeMessageClient;
    }

    if (clientSocket->bytesAvailable() < sizeMessageClient) {
        return;
    }

    sizeMessageClient = 0;

    in >> data;
/*
     I don't know the type of the received data !!

    - if I receive string        => call function showData(QString data);
    - if I receive file          => call function saveFile(QFile file);
    - if I receive audio stream  => play audio stream
    - ... 
*/

}

CLIENT:

Client::Client()
{
    socket = new QTcpSocket(this);
    connect(socket, SIGNAL(readyRead()), this, SLOT(readyRead()));

    sizeMessageServer = 0;
}


void Client::readyRead()
{
    QDataStream in(socket);
    if (sizeMessageServer == 0)
    {
        if (socket->bytesAvailable() < (int)sizeof(quint16)) {
            return;
        }

        in >> sizeMessageServer;
    }

    if (socket->bytesAvailable() < sizeMessageServer) {
        return;
    }

    int messageReceived;
    in >> messageReceived;
    messageReceived = static_cast<int>(messageReceived);

    sizeMessageServer = 0;

    switch(messageReceived)
    {
        case 1:
            qDebug() << "send a File";
            sendFile();
            break;
        case 2:
            qDebug() << "send a string data";
            sendStringData();
            break;
        case 3:
            qDebug() << "stream audio to the server";
            streamAudioToServer();
            break;
        case n:
        // ...    
    }
}

I am not looking for a complete solution, all I am looking for is some guidance in the right direction.

See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

The implementation of your protocol doesn't fully leverage QDataStream in Qt 5.7. Here's how it might look now - it can be quite simple.

First, let's define the requests we know of:

enum class Req : quint32 {
    Unknown, String, File
};
Q_DECLARE_METATYPE(Req)
QDataStream & operator<<(QDataStream & ds, Req req) {
    return ds << (quint32)req;
}
QDataStream & operator>>(QDataStream & ds, Req & req) {
    quint32 val;
    ds >> val;
    if (ds.status() == QDataStream::Ok)
        req = Req(val);
    return ds;
}

It'd also be handy to have a transaction RAII helper.

struct Transaction {
    QDataStream & stream;
    Transaction(QDataStream & stream) : stream{stream} {
        stream.startTransaction();
    }
    ~Transaction() {
        stream.commitTransaction();
    }
    bool ok() {
        return stream.status() == QDataStream::Ok;
    }
};

The client receives requests from the server and signals the need to reply with data. The code that uses the client would react to these signals and reply back by invoking a matching slot. E.g.

void clientUser(Client & client) {
  QObject::connect(&client, &Client::needString, &client, [&]{
    client.sendString(QStringLiteral{"You got string!"});
  });

And:

class Client : public QObject {
    Q_OBJECT
    QIODevice & m_dev;
    QDataStream m_str{&m_dev};
    void onReadyRead() {
        Transaction tr{m_str};
        Req req;
        m_str >> req;
        if (!tr.ok()) return;
        if (req == Req::String)
            emit needString();
        else if (req == Req::File) {
            QString fileName;
            m_str >> fileName;
            if (!tr.ok()) return;
            emit needFile(fileName);
        }
        else emit unknownRequest(req);
    }
public:
    Client(QIODevice & dev) : m_dev{dev} {
        connect(&m_dev, &QIODevice::readyRead, this, &Client::onReadyRead);
    }
    Q_SIGNAL void unknownRequest(Req);
    Q_SIGNAL void needString();
    Q_SIGNAL void needFile(const QString & fileName);
    Q_SLOT void sendString(const QString & str) {
        m_str << Req::String << str;
    }
    Q_SLOT void sendFile(const QString & fileName, const QByteArray & data) {
        m_str << Req::File << fileName << data;
    }
};

The server is very similar. Its user sends the request to a client via request slots. Once the server hears back from the client, it indicates it through the has signals:

class Server : public QObject {
    Q_OBJECT
    QIODevice & m_dev;
    QDataStream m_str{&m_dev};
    void onReadyRead() {
        Transaction tr{m_str};
        Req req;
        m_str >> req;
        if (!tr.ok()) return;
        if (req == Req::String) {
            QString str;
            m_str >> str;
            if (!tr.ok()) return;
            emit hasString(str);
        }
        else if (req == Req::File) {
            QString fileName;
            QByteArray data;
            m_str >> fileName >> data;
            if (!tr.ok()) return;
            emit hasFile(fileName, data);
        }
        else emit hasUnknownRequest(req);
    }
public:
    Server(QIODevice & dev) : m_dev{dev} {
        connect(&m_dev, &QIODevice::readyRead, this, &Server::onReadyRead);
    }
    Q_SIGNAL void hasUnknownRequest(Req);
    Q_SIGNAL void hasString(const QString &);
    Q_SIGNAL void hasFile(const QString & name, const QByteArray &);
    Q_SLOT void requestString() {
        m_str << Req::String;
    }
    Q_SLOT void requestFile(const QString & name) {
        m_str << Req::File << name;
    }
};

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...