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.
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;
}
};
来源:https://stackoverflow.com/questions/39144556/qt-sending-multiple-data-types-from-client-to-server-data-streaming