2017-06-13 5 views
1

Je suis nouveau à Qt et je suis un peu en difficulté. J'essaye d'envoyer une chaîne d'un client à un serveur en utilisant QTcpSocket.QTcpSocket :: readAll() est vide

côté client:

QByteArray block; 
QDataStream out(&block, QIODevice::WriteOnly); 
out.setVersion(QDataStream::Qt_4_0); 
out << name; 

tSock->connectToHost(ipAddress, portNumb.toInt()); 
tSock->waitForConnected(); 
tSock->write(block); // block is not empty 
tSock->flush(); 

côté serveur:

void Server::readyRead() 
{ 
    QByteArray block; 
    QDataStream out(&block, QIODevice::ReadOnly); 
    out << tcpSocket->readAll(); 
    QString name(block); // block is empty 
    players.insert(name, tcpSocket); 
    std::cout << "name: " << name.toStdString(); // TODO remove 
} 

Au programme côté serveur entre dans readyRead() lorsque les données sont reçues, mais le bloc est vide, bien que sur le bloc côté client n'est pas vide quand tSock->write(block) est appelé ... Où semble être le problème? J'apprécierais toute aide. Merci!

Répondre

2

Edit: Votre erreur est que vous ouvrez le flux de données out en mode lecture seule, mais essayez de écrire le tableau d'octets reçu à elle:

void Server::readyRead() 
{ 
    QByteArray block; 
    QDataStream out(&block, QIODevice::ReadOnly); // !mistake, WriteOnly mode is needed 
    out << tcpSocket->readAll(); // this is write operation 
    //... 
} 

supplémentaires: s'il vous plaît noter qu'il ya le Serialization mechanism of Qt Data Types qui est utile dans de tels cas:

tSock->write(block); // this is write just a raw data of the block, not the "QByteArray" 

Vous pouvez utiliser une opération de flux pour écrire les types de données Qt nécessaires à une prise directement, sans convertion à QByteArray:

// Connect firstly 
tSock->connectToHost(ipAddress, portNumb.toInt()); 
tSock->waitForConnected(); 

// Then open a data stream for the socket and write to it: 
QDataStream out(tSock, QIODevice::WriteOnly); 
out.setVersion(QDataStream::Qt_4_0); 
out << name; // write string directly without a convertion to QByteArray 

// Then you may 
tSock->flush(); 

côté client, puis utilisez l'opération de flux similaire sur le côté serveur :

void Server::readyRead() 
{ 
    QString name; 
    QDataStream in(tcpSocket, QIODevice::ReadOnly /*or QIODevice::ReadWrite if necessary */); 
    in.setVersion(QDataStream::Qt_4_0);  
    in >> name; // read the string 
    //... 
} 

Il est également possible de lecture/écriture objets i/périphériques o d'une autre Qt: QFile, QSerialPort, QProcess, QBuffer et d'autres.

Modifier 2: il n'est pas garanti que sur le signal readyRead vous recevrez le package complet qui a été envoyé. Par conséquent, voir l'exemple ci-dessous.


S'il vous plaît noter, que dans le cas réel (lorsque vous avez plusieurs paquets différents dans la communication client-serveur, et on ne sait pas quel genre de plusieurs paquets possibles que vous avez reçu) habituellement on utilise algorithme plus complexe, car les situations suivantes peuvent se produire sur l'événement readyRead dans une communication:

  1. paquet reçu
  2. pleine
  3. reçu qu'une partie du paquet
  4. a reçu plusieurs paquets ensemble

la variante de l'algorithme (Qt 4 Fortune Client Example):

void Client::readFortune() // on readyRead 
{ 
    QDataStream in(tcpSocket); 
    in.setVersion(QDataStream::Qt_4_0); 

    if (blockSize == 0) { 
     if (tcpSocket->bytesAvailable() < (int)sizeof(quint16)) 
      return; 

     in >> blockSize; 
    } 

    if (tcpSocket->bytesAvailable() < blockSize) 
     return; 

    QString nextFortune; 
    in >> nextFortune;  

    //... 
} 

Qt 4.0 est une version trop ancienne de Qt, donc voir aussi Qt 5.9 Fortune Client Example

+0

D'une manière générale, recréant un 'QDataStream' sur une prise ou est une erreur, car vous perdrez l'état du cours d'eau, et peut perdre des données ainsi que. –

+0

@KubaOber, mais ceci est un exemple de la documentation Qt 4, et je suis sûr que le code de la fonction 'readFortune()' ci-dessus est correct –

+0

Il est correct tel quel, mais il deviendra facilement incorrect dès que vous modifiez-le. –

0

Il semble que votre serveur n'a pas assez de temps pour écrire les données du client. s'il vous plaît essayer ..

tcpsocket->waitForBytesWritten(1000); 
0

Le code readyRead remplit incorrectement le block. Vous supposez également que vous lirez suffisamment de données pour transmettre la chaîne entière. Ce n'est jamais garanti. Tout ce que vous savez quand readyRead déclenche qu'il y a au moins un octet à lire. Le système de transaction QDataStream aide avec cela.

Ce serait une bonne mise en œuvre:

class Server : public QObject { 
    Q_OBJECT 
    QPointer<QTcpSocket> m_socket; 
    QDataStream m_in{m_socket.data(), &QIODevice::ReadOnly}; 
    void onReadyRead(); 
public: 
    Server(QTcpSocket * socket, QObject * parent = {}) : 
    QObject(parent), 
    m_socket(socket) 
    { 
    connect(socket, &QIODevice::readyRead, this, &Server::onReadyRead); 
    } 
} 

void onReadyRead() { 
    while (true) { // must keep reading as long as names are available 
    QString name; 
    in >> name; 
    if (in.commitTransaction()) { 
     qDebug << name; 
     players.insert(name, &m_socket.data()); 
    } else 
     break; 
    } 
};