socket servidor que con comando cliente bloqueante af_unix c++ qt client-server qtcpsocket qtcpserver

que - socket en c++ cliente servidor linux



Qt, envío de múltiples tipos de datos de cliente a servidor+transmisión de datos (1)

Tengo una aplicación Qt basada en Cliente / Servidor , usando QTcpServer y QTcpSocket, logré hacer la conexión y enviar algunos datos entre el cliente y el servidor. El cliente envía muchos tipos de datos al servidor (cadena, int, archivos y una transmisión de audio en tiempo real) y dado que mi servidor implementa una sola entrada de datos SLOT (readyRead ()):

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

No sé cómo podría distinguir entre todos estos datos recibidos y llamar respectivamente a la función correcta en el servidor.

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 - ...

SERVIDOR:

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 - ... */ }

CLIENTE:

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

No estoy buscando una solución completa, todo lo que busco es una guía en la dirección correcta.


La implementación de su protocolo no aprovecha al QDataStream en Qt 5.7. Así es como podría verse ahora: puede ser bastante simple.

Primero, definamos las solicitudes que conocemos:

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

También sería útil tener una transacción RAII helper.

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

El cliente recibe solicitudes del servidor y señala la necesidad de responder con datos. El código que usa el cliente reaccionaría a estas señales y respondería al invocar una ranura correspondiente. P.ej

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

Y:

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

El servidor es muy similar. Su usuario envía la solicitud a un cliente a través de espacios de request . Una vez que el servidor escucha al cliente, lo indica a través de las señales de la 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; } };