2013-05-20 1 views
2

J'ai l'extrait de code suivant dans le corps d'une boucle responsable de la lecture des données d'un QTcpSocket (nntp est un pointeur vers un QTcpSocket).Pourquoi mon code ne peut-il pas lire le nombre d'octets disponibles sur un QTcpSocket? Des idées?

std::vector<char> buffer; 
int bytesAvailable = nntp->bytesAvailable(); 
qDebug() << "bytesAvailable: "<<bytesAvailable; 
if(bytesAvailable <= 0) break; 
buffer.resize(bytesAvailable); 
bytesRead = nntp->read(&buffer[0], bytesAvailable); 
qDebug() << (nntp->state() == QAbstractSocket::ConnectedState); 
qDebug() << "bytesRead: "<<bytesRead; 

Intermittences cette commande affiche quelque chose de semblable à ce qui suit:

bytesAvailable: 24 
true 
bytesRead: 0 

Et mon code de là mis-se comporte. Cela me semble très étrange et suggère que je me méprends complètement sur le fonctionnement de QTcpSockets. Sûrement si bytesAvailable> 0 alors une lecture subséquente signifiera que bytesAvailable octets peuvent être lus dans un tampon auquel cas bytesRead devrait == bytesAvailable. Ou est-ce que je manque quelque chose? Mon soupçon actuel est qu'il pourrait s'agir d'une corruption de mémoire.

EDIT: L'envoi dans certains messages nntp.errorString() signale que, lors de cet incident, le message "Expiration du réseau a expiré". J'ai besoin d'étudier ce que cela signifie ... (idées?)

EDIT 2: Il semble que "L'opération réseau a expiré" signifie simplement que la lecture a expiré. Lorsque le code fonctionne comme il le devrait (c'est-à-dire par intermittence), je reçois toujours cette 'erreur'.

EDIT 3: Le contexte algorithmique complet pour l'extrait de code ci-dessus peut être trouvé at this pastebin link.

EDIT 4: Une version légèrement différente de la fonction EDIT 3, mais toujours avec les mêmes problèmes sont néanmoins à this newer pastebin link

+0

Je n'aime vraiment pas cette construction: '& buffer [0]'. Au lieu de cela, essayez 'buffer = nntp-> readAll();'. – Amartel

+0

Merci pour votre suggestion Amartel mais en passant à un QByteArray et ensuite faire un readAll, puis en utilisant la taille de la QByteArray pour déterminer le nombre d'octets lus encore entraîne le même problème –

Répondre

3

J'ai découvert le problème: le QTcpSocket que j'avais essayé de lire appartenait à un thread différent. Même si les octets étaient disponibles selon le tampon de QIODevice, ils ne pouvaient pas être lus à cause de cela.

Par conséquent:

* Assurez-vous toujours que la prise que vous voulez lire appartient au thread sur lequel vous voulez faire la lecture *

Pour aider à cela, et comme Taylor suggéré ci-dessus , on peut profiter de l'infrastructure des signaux et des slots de QTcpSocket (de préférence). Cela a l'infrastructure de threading appropriée en place et en théorie devrait rendre les choses beaucoup plus simples.

OU

super-prudent et

(a) utiliser les propres QThread, déplacer l'objet qui contient le QTcpSocket à ce QThread

puis,

(b) utiliser waitForReadyRead de QTcpSocket dans la boucle de lecture bloquante de cet autre thread. Cette dernière approche est plus difficile car si l'on veut conserver le QTcpSocket pour les autres threads dans les prochaines lectures et écritures, alors après avoir été déplacé vers un autre thread et traité par cet autre thread, il doit être déplacé vers le thread principal avant qu'il ne peut jamais être déplacé un autre thread. - me donne mal à la tête en essayant juste de dire ça! Bien sûr, on pourrait choisir de garder le même thread de travail QTcpSocket pour qu'il ne doive être déplacé qu'une seule fois, ou simplement créer un nouveau QTcpSocket chaque fois qu'un QTcpSocket est requis, le déplacer vers son propre QThread et le supprimer immédiatement quand il est fini avec.

Fondamentalement, optez pour la première méthode de signaux et de slots si vous le pouvez.

2

Je n'ai pas rencontré ce problème précis (peut-être parce que je suis en utilisant une version différente de Qt), mais je recommanderais d'essayer de passer d'une boucle à une approche axée sur les événements. Si votre boucle est en cours d'exécution dans le thread principal, il n'y a aucun moyen pour les objets de livrer des signaux en file d'attente (comme la classe QTcpSocket peut le faire en interne), ce qui explique peut-être l'expiration du délai d'attente.

connecter Alors quelques-uns des signaux QTcpSocket fondamentaux:

connect(nntp, SIGNAL(disconnected()), 
     this, SLOT(onDisconnected())); 
connect(nntp, SIGNAL(readyRead()), 
     this, SLOT(onReadyRead())); 
connect(nntp, SIGNAL(error(QAbstractSocket::SocketError)), 
     this, SLOT(onSocketError(QAbstractSocket::SocketError))); 

Et puis mettez votre code "lire" existant dans onReadyRead.

Peut-être que votre problème n'est pas lié, mais j'ai récemment vu des problèmes similaires dans le code d'un collègue a écrit, et voici comment je l'ai réparé.

+0

Merci Taylor - Je préfère aussi la façon de signaux et fentes de faire les choses et je peux changer à cette approche à l'avenir. La raison pour laquelle j'ai choisi une boucle est parce que j'ai besoin de la capacité de lire dans une quantité définie de données formatées qui est segmentée en fonction de plusieurs séquences d'échappement; dans la boucle, il est relativement facile d'analyser les données en fonction de ces jetons, et de plus, au moment précis où je sais qu'il devrait être disponible. Cela dit, mon approche est clairement boguée, donc je risque de devoir passer à SIGNAL et SLOTs comme vous le recommandez. –

2

Qu'arrive-t-probablement si ce bytesAvailable() indique la taille des données en attente dans la mémoire tampon interne QIODevice plus la taille rapportée par le système d'exploitation (par exemple sous Linux qui serait obtenu par ioctl(fd,FIONREAD,&bytesCount)).

Cela a du sens en soi, mais pour être capable de lire ces octets à partir d'un QTcpSocket sans la boucle d'événement, waitForReadyRead() doit être appelé dans votre propre boucle. Sinon, les données du tampon du noyau ne le font pas dans le tampon de QIODevice. Si votre code n'est pas autorisé à bloquer lorsqu'il n'y a rien à lire sur le socket, et que vous ne voulez pas le transformer en une structure pilotée par les événements, utilisez une petite valeur pour le timeout de waitForReadyRead afin que dans la pratique il se comportera comme s'il ne bloquait pas.

N'oubliez pas d'appeler également le waitForBytesWritten() si vous écrivez également sur le socket sans boucle d'événement.

+0

Merci Daniel, c'est logique. J'utilise en fait waitForReadyRead dans le contexte de la boucle complète. Voir EDIT 3 dans le message original pour un lien. –

+0

Je le déplacerais de la boucle externe à juste avant de lire 'bytesAvailable()' pour voir si cela fait une différence, d'autant plus que la boucle externe est un do..while par opposition à un certain temps ..do –

+0

Merci Daniel, J'ai essayé votre suggestion (voir le code au lien EDIT 4) mais j'ai toujours le même problème –

Questions connexes