简体   繁体   English

Qt,从客户端到服务器发送多种数据类型+数据流

[英]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. 我有一个基于客户端/服务器Qt应用程序,使用QTcpServer和QTcpSocket,我设法进行了连接,并在客户端和服务器之间来回发送了一些数据。 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()): 客户端向服务器发送多种类型的数据(字符串,整数,文件和实时音频流),并且由于我的服务器隐含了一个数据输入插槽(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. 我不是在寻找完整的解决方案,而是在寻找正确方向的指导。

The implementation of your protocol doesn't fully leverage QDataStream in Qt 5.7. 您的协议的实现并未完全利用Qt 5.7中的QDataStream 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. 拥有交易RAII助手也很方便。

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. 使用客户端的代码将对这些信号做出反应,并通过调用匹配的插槽进行回复。 Eg 例如

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. 它的用户通过request槽将请求发送到客户端。 Once the server hears back from the client, it indicates it through the has signals: 服务器从客户端收到回音后,便通过has信号指示它:

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

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM