2016-08-25 6 views
1

У меня есть Client/Server на основе Qt приложения, используя QTcpServer и QTcpSocket, мне удалось сделать подключение и отправить некоторые данные вперед и назад между клиента и сервера. Клиент посылает множество типов данных на сервер (String, Int, файлов и аудио потока в режиме реального времени), а с моего сервера impliment один ввода данных SLOT (readyRead()):Qt, Отправка нескольких типов данных от клиента на сервер + потоковые данные

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

Я DON» я знаю, как я мог различать все эти полученные данные и соответственно называть правильную функцию на сервере.

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() 
{ 
    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: 
     // ...  
    } 
} 

Я не ищу для полного решения, все, что я ищу некоторые рекомендации в правильном направлении ,

+2

Похоже, вам нужно придумать (или использовать существующий) * протокол *, который может сказать вам, какие данные вы передаете. –

+0

Я не могу найти в сети какой-либо пример того, как я могу это сделать .. –

+2

Как минимум, вы можете упаковать тип и значение в сообщении, а затем на конечный выключатель приема на тип. –

ответ

2

Реализация вашего протокола не полностью использует QDataStream в Qt 5.7. Вот как это могло бы выглядеть сейчас - это может быть довольно просто.

Во-первых, давайте определим запросы, которые мы знаем из:

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

Было бы также удобно иметь транзакции RAII помощника.

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

Клиент получает запросы от сервера и сигнализирует о необходимости ответа на данные. Код, который использует клиент, будет реагировать на эти сигналы и отвечать назад, вызывая соответствующий слот. Например.

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

И:

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

Сервер очень похож. Его пользователь отправляет запрос клиенту через слоты request. После того как сервер слышит обратно от клиента, это указывает на его через 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; 
    } 
};