[gelöst] Zuviel Daten für QTcpSocket?

Alles rund um die Programmierung mit Qt
Antworten
Rumbert
Beiträge: 48
Registriert: 25. Mai 2009 18:28
Wohnort: Witten

[gelöst] Zuviel Daten für QTcpSocket?

Beitrag von Rumbert »

Hallo miteinader!

Ich habe ein Gerät welches mir Images liefert (etwa 15kb große jpgs und das 30 mal pro Sekunde). Diese Bilder sollen dann von meiner Serverapplikation an diverse Clients gesendet werden. Der Client rendert diese in OpenGL auf eine Textur. Sofern ich nur jedes 10 Bild verschicke läuft alles einwandfrei. Verschicke ich nun jedes Bild, dann werden nach etwa 20 sekunden keine Bilder mehr übertragen (das empfangen QImage ist dann immer NULL).

Hier mein Codesnippet von der Serverseite:

Code: Alles auswählen

void VideoServer::slotSendMessage(const QImage& image)
{
	QByteArray baImage;
	QByteArray block;
	QBuffer buffer(&baImage);
	buffer.open(QIODevice::WriteOnly);
	image.save(&buffer, "JPG"); 

	for (int i = 0; i < connectedSockets.count(); ++i)
	{
		QDataStream out(&block, QIODevice::WriteOnly);
		out.setVersion(QDataStream::Qt_4_4);
		qDebug() << (quint16)baImage.size();
		out << (quint16)baImage.size();
		out << baImage; 
		connectedSockets[i]->write(block);
   }
}
und auf der Clienseite:

Code: Alles auswählen


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

void VideoClient::slotReciveMessage()
{
	QImage image;
	int i = 0;
	QDataStream stream(&socket);
	stream.setVersion(QDataStream::Qt_4_4);
 	while (!stream.atEnd())
	{
		stream >> image;
		i++;
	}
	qDebug()<< i;
	emit signalRenderCamImage(image);
}
Meine erste Idee war, dass der Puffer des Sockets zu voll wird (oder sowas ) und dachte ich könnte mit socket.setReadBufferSize die Puffergröße so reduzieren, dass die Bilder einfach verloren gehen, wenn sie nicht abgearbeitet werden können. Aber das brachte keine Änderung.
Auffällig ist, dass der Wert i immer 1 ist wenn ein QImage nicht Null ist (also ein Bild erfolgreich beim Client ankommt), und anderenfalls nen Wet von mehreren tausend hat, das also stream.atEnd() erst mehrere Tausendmal durchlaufen werden muss.
Auch wenn der Server schon längst gestoppt ist wird auf der Clientseite immer wieter ein readyRead emmitiert. Mit Wireshark kann ich leider nicht den Traffic von 127.0.0.1 beobachten, um evtl weitere Infos zu bekommen.
Hat jemadn ne Idee was ich da machen kann, oder ob mein Ansatz grundsätzlich falsch ist?

Bin für alle Ideen dankbar!
Grüße Rumbert
Zuletzt geändert von Rumbert am 16. Juni 2009 17:37, insgesamt 1-mal geändert.
solarix
Beiträge: 1133
Registriert: 7. Juni 2007 19:25

Beitrag von solarix »

natürlich gibt es (je nach Hardware) eine obere Grenze, aber du bist da (wenn es wirklich nur 30x15kb sind) vermutlich noch weit entfernt.

Ich glaube eher, dein Netzwerkcode funktioniert nicht richtig, weil du da kein Protokoll definiert hast. Du musst eine TCP-Verbindung als "Fluss" betrachten. Das bedeutet, dass wenn ein "readyRead"-Signal kommt, weisst du nicht, wieviele Daten da drin sind (1/2 Bild, ein ganzes Bild, oder 2.99 Bilder.. das weisst du einfach nicht). Daher, musst du selbst ein Protokoll definiert. Ein einfaches wäre:

Sender:
1. serialisiere Bild
2. sende Länge der serialisierten Daten via Socket
3. sende serialisierte Daten

Server (bei Signal):
1. lese Laenge der serialisierten Daten von Socket
2. Warte solange, bis mindestens diese Länge im Buffer vorhanden ist (bytesAvailable)
3. lese genau diese Länge aus dem Socket
4. konvertiere serialisierte Daten wieder zurueck in ein Bild

hth..
Rumbert
Beiträge: 48
Registriert: 25. Mai 2009 18:28
Wohnort: Witten

Beitrag von Rumbert »

Hallo solarix,

bezüglich des Protokolls, bei der Sender Seite dachte ich das schon so in etwa zu machen:

1. Serialisieren des Bildes:

Code: Alles auswählen

QByteArray baImage;
QByteArray block;
QBuffer buffer(&baImage);
buffer.open(QIODevice::WriteOnly);
image.save(&buffer, "JPG"); 
2. sende Länge der Bild Daten

Code: Alles auswählen

QDataStream out(&block, QIODevice::WriteOnly);
out.setVersion(QDataStream::Qt_4_4);
qDebug() << (quint16)baImage.size();
out << (quint16)baImage.size();
3. senden der Bilddaten an sich

Code: Alles auswählen

out << baImage; 

connectedSockets[i]->write(block);
oder muss ich die beiden Nachrichten (Länge der Bilddaten und Bilddaten ansich ) in separaten QByteArrays senden, sprich für jedes "out<<" ein eigenes "write(block)"?

Oder habe ich das gänzlich missverstanden mit dem Protokoll auf der Sender Seite?
Was die Empfängerseite angeht, da tu ich mich noch etwas schwer mit und wollte zuerst mal die Senderseite klären.

Vielen Dank für Deine Hilfe!
Grüße Rumbert
solarix
Beiträge: 1133
Registriert: 7. Juni 2007 19:25

Beitrag von solarix »

Oder habe ich das gänzlich missverstanden mit dem Protokoll auf der Sender Seite?
Neinnein.. das hast du nicht.. das passt grundsätzlich schon. Ich persönlich würde nicht auch noch den ersten Integer serialisieren. Das ist aber mehr ein Gefühl, weil ich nicht genau weiss was mit dem QDataStream geschieht, wenn es unvollständig beim Server empfangen wird. Die Länge der Nutzdaten sende ich entweder plattformunabhängig gewandelt als String (ASCII, fixlänge) oder direkt (binär.. little-/bigendian beachten).
Was die Empfängerseite angeht, da tu ich mich noch etwas schwer
Das ist auch etwas schwerer, weil das halt zerstückelt ankommt ;)
Versuch das Gegenstück einfach mal und zeig uns wieder den Code..
Rumbert
Beiträge: 48
Registriert: 25. Mai 2009 18:28
Wohnort: Witten

Beitrag von Rumbert »

So nach dem Wochenende habe ich mich heute nun an die Empfänger-Seite versucht, mit leider nur mäßigem Erfolg...

Also ich Sende nun wie Folgt erst die Bildgröße (ohne den Integer in den Buffer zu schreiben ) und dann das Image (im Buffer):

Code: Alles auswählen

 QByteArray baImage;
 QByteArray block;
 QBuffer buffer(&baImage);
 buffer.open(QIODevice::WriteOnly);
 image.save(&buffer, "JPG"); 

		// Übertragen der Bildgröße
		QDataStream directOut(connectedSockets[i]);
		directOut.setVersion(QDataStream::Qt_4_4);
		directOut << (quint16)baImage.size();
		qDebug() << (quint16)baImage.size();

		// übertragen des Bildes
		QDataStream out(&block, QIODevice::WriteOnly);
		out.setVersion(QDataStream::Qt_4_4);
		out << baImage; 
		connectedSockets[i]->write(block);
Dann habe ich dazu den Empfänger wie folgt realisiert:

Code: Alles auswählen

Videoclient::slotReciveMessage()
{
	QImage image;
	quint16 incommingBlockSize = 0;
	QDataStream stream(&socket);
	stream.setVersion(QDataStream::Qt_4_4);
	qint64 aommunt = socket.bytesAvailable();
	
	if (socket.bytesAvailable()<(int)sizeof(quint16))
		return;
	stream >> incommingBlockSize;
	qDebug() << incommingBlockSize;

	if (socket.bytesAvailable()<incommingBlockSize)
		return;
	QByteArray imageArray = socket.read( incommingBlockSize );
	image.fromData(imageArray);

	emit signalRenderCamImage(image);
}
Beim ersten Aufruf des Videoclients ist die ImageSize im Sender und Empfänger gleich und entspicht den Werten der Testweise vor dem Absenden auf Platte gespeicherteten Bildern (etwa 12366 Bytes) während bei dem Aufrauf bytesAvailable 12372 liefert.
Trotz dieser für mich plausieblen Ausgaben ist so nun das QImage NULL :-(
Bei den nächsten Iterationen des Slots sind deutlich mehr bytesAvailable, seltsamer weise ist ide incommingBlocksize dann nicht mehr wie die von der Sende-Seite, dass heißt er interpretiert irgendwas in dem Datenstrom als die Blocksize und image ist dann auch weiterhin NULL...
Also irgendwie bin ich noch recht weit weg von meinem Ziel ein kleines stabiles Protokoll für die Bilderübertragung zu entwerfen.
Was mich wundert, dass meine ersten Version (das Eröffnungspost) bei heruntergefahrener Framerate stabil läuft...

Ich bin für jede Hilfe sehr dankbar!

Grüße Rumbert
Christian81
Beiträge: 7319
Registriert: 26. August 2004 14:11
Wohnort: Bremen
Kontaktdaten:

Beitrag von Christian81 »

Warum liest Du immer wieder incommingBlockSize aus? Das kann nichts werden wenn Du davor wegen zu wenig Daten zurückgegangen bist...
MfG Christian

'Funktioniert nicht' ist keine Fehlerbeschreibung
Rumbert
Beiträge: 48
Registriert: 25. Mai 2009 18:28
Wohnort: Witten

Beitrag von Rumbert »

Ok die Anmerkung leuchtet ein, aber dieser Fall trat bisher noch nicht auf (siehe neues Codebeispiel). Insebsondere, wenn ich das erste mal den Slot durchlaufe, wo die Werte im Debugger noch vernünfitg aussehen, ist QImage trotzdem Null.

Code: Alles auswählen

// compleeted ist nen bool Member der im Konstruktor auf true gesetzt wird.
 if (compleeted)
	{
		if (socket.bytesAvailable()<(int)sizeof(quint16))
			return;
		stream >> incommingBlockSize;
		qDebug() << incommingBlockSize;
		compleeted = false;
	}

	if (socket.bytesAvailable()<incommingBlockSize)
		return;
	QByteArray imageArray = socket.read( incommingBlockSize );
	image.fromData(imageArray);
	compleeted = true;
       emit signalRenderCamImage(image);
Da jedoch der Fall (socket.bytesAvailable()<incommingBlockSize) nie bei mir eintritt muss mein eigentliches Problem noch woanders liegen. :(

Grüße Rumbert
Christian81
Beiträge: 7319
Registriert: 26. August 2004 14:11
Wohnort: Bremen
Kontaktdaten:

Beitrag von Christian81 »

Du schickst das QImage über einen Datastream, liest es aber ohne Datastream aus - das kann imho auch nicht funktionieren. Entweder mit oder ohne Datastream (an der Stelle würde ich sagen - QDatastream komplett weglassen).
MfG Christian

'Funktioniert nicht' ist keine Fehlerbeschreibung
Antworten