2016-08-25 21 views
1

我有一個客戶端/服務器基於Qt的應用程序,使用QTcpServer既可並與QTcpSocket多種數據類型,從客戶端到服務器+數據,我能夠做到的連接,發送一些數據來回之間客戶端和服務器。 客戶端發送多種類型的數據服務器(字符串,整數,文件和實時音頻流),並且由於我的服務器impliment單個數據輸入插槽(readyRead()):Qt中,發送流

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

我不我知道如何區分所有這些接收到的數據並分別調用服務器中的正確功能。

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

您的協議的實施並未充分利用Qt 5.7中的QDataStream。以下是它現在的樣子 - 它可以非常簡單。

首先,讓我們來定義我們所知道的請求:

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