QByteArray per TcpSocket verschicken

Alles rund um die Programmierung mit Qt
Viper2000
Beiträge: 48
Registriert: 7. Mai 2008 16:36

QByteArray per TcpSocket verschicken

Beitrag von Viper2000 »

Hallo,

ich versuche verzweifelt ein QByteArray über einen Socket zu schicken.

Hier der "Server-Code":

Code: Alles auswählen

bool Thread::sendData(QByteArray data)
{
		QByteArray block;
		QDataStream out(&block, QIODevice::WriteOnly);
		out.setVersion(QDataStream::Qt_4_3);
		out << (quint32)0;
		out << data;
		out.device()->seek(0);
		out << (quint32)(block.size() - sizeof(quint32));
               tcpSocket.write(block);
Das ist soweit das Beispiel aus dem Fortune Server. Wenn ich nen QString statt des QByteArray schicke, dann kann ich den auf der anderen Seite auch wieder prima extrahieren aber mein QByteArray kommt auf der anderen Seite zwar an, bzw. es kommt irgendwas an, aber die Daten sind nicht korrekt.
Der QDataStream schreibt ja an sich schon in ein QByteArray und mein ByteArray wird in den Stream eingebettet. Anscheinend hat er aber da probleme mit.

Hier der "Client-Code:"

Code: Alles auswählen

void Thread::readReceivedData()
{
	QByteArray receivedData;
	mBlockSize = 0;

	QDataStream in(&mTcpSocket);
	in.setVersion(QDataStream::Qt_4_3);
		
	if(mBlockSize == 0)
	{
		if(mTcpSocket.bytesAvailable() < (int)sizeof(quint32))
		{
			return;
		}
		
		in >> mBlockSize;
	}
		
	if (mTcpSocket.bytesAvailable() < mBlockSize)
		return;
	
	in >> receivedData;
Hat jemand ne Ahnung warum das nicht funktioniert!?

MfG viper2000
Ich bin nicht die Signatur, ich putz hier nur :-)
solarix
Beiträge: 1133
Registriert: 7. Juni 2007 19:25

Beitrag von solarix »

aber die Daten sind nicht korrekt.
Solche Fehlerbeschreibungen sind immer wieder schwierig für uns.. Benutze Debugger und Wireshark (http://www.wireshark.org/) um den Fehler exakt beschreiben zu können (z.B. "Server versendet Daten korrekt, Client empfangt zuwenig").

Noch was Grundsätzliches: Die Server-Daten werden vom BS in Frames aufgeteilt und stückchenweise versendet. Falls die Daten also nicht in ein einziges Frame passen (was vermutlich bei deinem Beispiel der Fall ist, beim String-Test jedoch noch nicht), musst du solange lesen, bis du den ganzen Block empfangen hast. Dies kannst du auf zwei Arten tun, entweder in Form eines Loops (solange waitForReadyRead() bis bytesAvailable() deiner Block-Grösse entspricht) oder -der schönere Weg- über einen Empfangs-Slot, der alle Daten stückchenweise (immer wenn was neues da ist) in eine private Variable liest.

Falls deine readReceivedData() ein Slot ist (verbunden mit readyRead ()) ist, wird sie mit jedem neu eintreffenden Daten-Abschnitt erneut aufgerufen...

Noch was kleines...vielleicht überseh ich was, aber bei

Code: Alles auswählen

 out << (quint32)0;
 out << data;
 out.device()->seek(0);
 out << (quint32)(block.size() - sizeof(quint32)); 
Warum nicht einfach?

Code: Alles auswählen

 out << (quint32)data.size();
 out << data;
PeterLustig
Beiträge: 386
Registriert: 21. November 2007 20:07

Beitrag von PeterLustig »

Warum benutzt du C-Sprachelemente in C++? Das ist ganz schlechter Stil. :[
Viper2000
Beiträge: 48
Registriert: 7. Mai 2008 16:36

Beitrag von Viper2000 »

Ja, meine Empfangsmethode ist ein SLOT der mit dem readyRead() SIGNAL des Sockets verbunden ist. Meine Empfangsroutine entspricht weitestgehend dem "Fortune Client Example" aus der Trolltech Qt Dokumentation, kann so schlecht also nicht sein denke ich :)



Wo benutze ich denn C-Sprachelemente?
Warum nicht einfach?
Code:

out << (quint32)data.size();
out << data;
Ja das könnte man natürlich machen... :)
Ich bin nicht die Signatur, ich putz hier nur :-)
PeterLustig
Beiträge: 386
Registriert: 21. November 2007 20:07

Beitrag von PeterLustig »

Du benutzt C-Style-Casts.
Viper2000
Beiträge: 48
Registriert: 7. Mai 2008 16:36

Beitrag von Viper2000 »

meinste das hier z.B:

out << (quint32)(block.size() - sizeof(quint32));

aber wieso nutzt Trolltech in ihren Offiziellen Beispielen denn diesen Stil wenn es so schlecht ist? Bzw. wie kann mans "besser machen"
Ich bin nicht die Signatur, ich putz hier nur :-)
solarix
Beiträge: 1133
Registriert: 7. Juni 2007 19:25

Beitrag von solarix »

Meine Empfangsroutine entspricht weitestgehend dem "Fortune Client Example" aus der Trolltech Qt Dokumentation, kann so schlecht also nicht sein denke ich
Das dachten die Jungs bei der ESA auch, als sie den Code von der Ariane4 auf Ariane5 kopierten und diese damit schrotteten..

Hast du meine Hinweise verstanden und umgesetzt, oder erwartest du weitere Blicke in die Kristallkugel?
Viper2000
Beiträge: 48
Registriert: 7. Mai 2008 16:36

Beitrag von Viper2000 »

Hi Solarix,

ja sorry hatte die ganze Zeit getestet. Es war ein Bug im Client...hatte die empfangene Blockzize dort noch als quint16 drinnne. Hab jetzt alles auf den 32 Bit Wert geändert und nun kommt mein Array einwandfrei auf der anderen Seite an :)

Wenn ich deinen Hinweis mit den Frames richtig interpretiert habe dann sollte doch mit meiner Empfangsroutine wegen des connects readyReady() <-> readDeceivedData() in jeden´m Fall der Empfang gesichert sein - auch wenn die Daten auf mehrere Frames verteilt sind!?
Ich bin nicht die Signatur, ich putz hier nur :-)
Maxima
Beiträge: 27
Registriert: 23. Mai 2008 03:45

Beitrag von Maxima »

Hi Viper2000,

Du schickst ja die datenlänge vorneweg, dh. Dein client kann sich darauf "einstellen" genau diese datenmenge zu empfangen; indem der client die ankommenden (Signal readyRead)Daten bis zu der datenlänge einem ByteArray zuweist. Die nächsten (darüber hinaus gehenden)Daten müssen also einem neuen Datensatz angehören.

Es kann sein das der server zwei datenblöcke in mehreren Paketen direkt hintereinander schickt und die Grenze zwischen den Beiden in einem Paket ankommen. Das muß man abfangen.
Viper2000
Beiträge: 48
Registriert: 7. Mai 2008 16:36

Beitrag von Viper2000 »

@Maxima: Ich denke das fange ich doch ab, da ich Byteweise Nachschaue was so am Socket ankommt. Auch wenn der Server 2 Datensätze schnell hintereinander schickt sollte das doch kein Problem darstellen!?

Falls ich das irgendwie missverstehen sollte wäre ich dankbar um ein Stück Beispielcode der das von dir gemeinte Problem aufgreift.

Schönes Wochenende schonmal @all
Ich bin nicht die Signatur, ich putz hier nur :-)
Maxima
Beiträge: 27
Registriert: 23. Mai 2008 03:45

Beitrag von Maxima »

Dein Code
(habe ich in der letzten zeile kommentiert)

Code: Alles auswählen

void Thread::readReceivedData()
{
   QByteArray receivedData;
   mBlockSize = 0;

   QDataStream in(&mTcpSocket);
   in.setVersion(QDataStream::Qt_4_3);
      
   if(mBlockSize == 0)
   {
      if(mTcpSocket.bytesAvailable() < (int)sizeof(quint32))
      {
         return;
      }
      
      in >> mBlockSize;
   }
      
   if (mTcpSocket.bytesAvailable() < mBlockSize)
      return;
   
   in >> receivedData;                           //<--hier achtest Du nicht auf die erwartete Größe (mBlockSize)
 
Mein Code:

Code: Alles auswählen

class{
...
quint32 waitingForBytes;
QByteArray receivedData; 
...
}

void Thread::readReceivedData()
{
   QDataStream in(&mTcpSocket);
   in.setVersion(QDataStream::Qt_4_3);
      
   if(waitingForBytes== 0)
   {
      if(mTcpSocket.bytesAvailable() < (int)sizeof(quint32))
      {
         return;
      }
      
      in >> waitingForBytes;
   }
      
   while (receivedData.size()< waitingForBytes && mTcpSocket.bytesAvailable() > 0){
         receivedData.append(mTcpSocket->read(1));
         waitingForBytes--;
    }//ich steige also aus, wenn ich alle Daten habe, die zu diesem ByteArray gehören
Hoffe ich habe es richtig getippt, aber Du wirst verstehen was ich meine

Allen ein schönes Wochenende
Viper2000
Beiträge: 48
Registriert: 7. Mai 2008 16:36

Beitrag von Viper2000 »

@Maxima:
Wie stellst du denn sicher, dass alles was du sendest auf der anderen Seite auch gelesen wird? Wenn ich z.B. in ner Schleife 1000 ByteArrays unmittelbar hintereinander schicke, dann wird auf der anderen Seite nur ca. 713 mal das ByteArray gelesen :(
Ich bin nicht die Signatur, ich putz hier nur :-)
Sephral
Beiträge: 201
Registriert: 1. Februar 2006 09:40
Kontaktdaten:

Beitrag von Sephral »

Viper2000 hat geschrieben:@Maxima:
Wie stellst du denn sicher, dass alles was du sendest auf der anderen Seite auch gelesen wird? Wenn ich z.B. in ner Schleife 1000 ByteArrays unmittelbar hintereinander schicke, dann wird auf der anderen Seite nur ca. 713 mal das ByteArray gelesen :(
Das klingt so als würden bei Dir Pakete zusammengefasst und in einem großen Paket übertragen (Passiert manchmal, wenn man zu schnell ist). Du findet dann 2 ByteArrays (oder mehr) in einem Paket (readyRead löst nur 1x aus). Das sollte sich mit einer Art "Trennzeichen" zwischen den Paketen lösen lassen.
Maxima
Beiträge: 27
Registriert: 23. Mai 2008 03:45

Beitrag von Maxima »

Das ist ein interessantes Problem das Du beschreibst!

Welche leseMethode benutzt Du denn jetzt? (Hast Du verstanden, was ich geposted habe?)

Zunächst würde ich überprüfen ob die datensätze correct empfangen werden (Größe/Inhalt).
Dann würde ich weniger Datensätze (auf einmal) schicken (100,200,300 etc) um zu sehen, ob der Buffer vielleicht nicht groß genug ist.

Dann würde ich die empfangsroutine noch ändern

Code: Alles auswählen

receivedData.append(mTcpSocket->read(1)); 
um mehr als 1 Byte zu lesen (könnte sein daß die Schleife zu langsam ist; unwahrscheinlich, weil die Daten ja gepuffert werden...)

"Sichern" läßt sich das nur, wenn man einen weiteren Parameter vorwegschicken würde, der die Anzahl der zu erwartenden Datensätze enthält, um eine FehlerMeldung zu produzieren.

Laß mich unbedingt wissen, was Du rausfindest.
Ich bin in das Problem noch nicht hineingelaufen :D

Spannender Thread!

EDIT:
@Sephral: good point! Der Slot darf nur verlassen werden, wenn bytesAvailable == 0; dh muß die Fähigkeit haben eventuell einen/mehere neue Bytearray anzulegen.
Das "Trennzeichen" braucht man nicht.
Habe ich in meinem Program so gemacht, aber nicht mehr erinnert...
Sephral
Beiträge: 201
Registriert: 1. Februar 2006 09:40
Kontaktdaten:

Beitrag von Sephral »

Maxima hat geschrieben: EDIT:
@Sephral: good point! Der Slot darf nur verlassen werden, wenn bytesAvailable == 0; dh muß die Fähigkeit haben eventuell einen/mehere neue Bytearray anzulegen.
Das "Trennzeichen" braucht man nicht.
Habe ich in meinem Program so gemacht, aber nicht mehr erinnert...
Ich verschicke bei mir keine ByteArrays, sondern relativ einfache Befehlte/Informationen (diese aber recht fix hintereinander). Es kommt häufiger vor, dass mehrere meiner Befehle dann letztendlich in einem Paket gesendet / empfangen werden auf der Gegenseite. Daher gibts bei mir nur ein einfaches Trennzeichen und der Empfänger nimmt die empfangenen Daten dann einfach mit split() auseinander.

Bei Eurem Vorgehen mit dem Voranstellen der Größe ist das Trennzeichen natürlich nicht notwenig.
Antworten