Binärdatei übertragen + QTcpSocket

Alles rund um die Programmierung mit Qt
Antworten
NoRulez
Beiträge: 208
Registriert: 19. Juni 2007 15:43

Binärdatei übertragen + QTcpSocket

Beitrag von NoRulez »

Hey @all,

ich habe einen client und server basierend auf den threaded fortune server und client.

Nun die Kommunikation zwischen den beiden funktioniert prima.
Mein Problem ist nun aber wie kann ich eine Datei übertragen?
Da das ganze ja Asynchon/Non-Blocking läuft, dachte ich mir während des Transfers in blocking umzuschalten und danach wieder zurück.
Nur falls das geht, wie mache ich das? Und falls es doch nicht geht, wie übertrage ich meine Datei(en) ohen ftp zu verwenden?

::send bzw. ::recv funktioniert leider nicht, da dann der SLOT readyRead() angesprochen wird :(

Bitte um Hilfe

Lg NoRulez
nando
Beiträge: 321
Registriert: 28. Oktober 2004 13:16

Beitrag von nando »

Moin,

wieso willst du blocking uebertragen ?
mit readyRead klappt das doch prima ohne zu blockieren...
du muesstest dir halt ein eigenes mini protokoll ausdenken....
der empfaenger muss ja erkennen, wann die zu uebertragenden binaerdaten zu ende sind....
du koennetest z.b immer zuerst paar bytes uebertragen, die angeben wie gross die datei oder was auch immer ist......dann weisst der client wieviele bytes er lesen muss (readyRead) und wann er eine datei etc. komplett empfangen hat....

gruss,
nando
NoRulez
Beiträge: 208
Registriert: 19. Juni 2007 15:43

Beitrag von NoRulez »

OK, also meine ready read sieht bisher wi folgt aus (client / server):

Code: Alles auswählen

void Client::onReadyRead() {
	if (tcpSocket == NULL) return;
	if (tcpSocket->state() != QAbstractSocket::ConnectedState) return;

	QDataStream in(tcpSocket);
	in.setVersion(QDataStream::Qt_4_2);
	//in.setVersion( in.version() ); // set to the current Qt version instead

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

	if(tcpSocket->bytesAvailable() < blockSize) return;
	QString msgString;
	QVariant msgData;
	in >> msgString;
	in >> msgData;

	emit onMessage(msgString, msgData);

	reading_message = false;
	blockSize = 0;

	if(tcpSocket->bytesAvailable() > 0) onReadyRead();
}

void Client::sendMessage(const QString &msg, const QVariant &data) {
	if(tcpSocket == NULL) return;
	if(tcpSocket->state() != QAbstractSocket::ConnectedState ) return;

	QByteArray block;
	QDataStream out(&block, QIODevice::WriteOnly);
	out.setVersion(QDataStream::Qt_4_2);
	//out.setVersion( out.version() ); // set to the current Qt version
	//out << (quint32) 0;
	out << (quint16) 0;
	out << msg;
	out << data;
	out.device()->seek(0);
//	out << (quint32)(block.size() - sizeof(quint32));
	out << (quint16)(block.size() - sizeof(quint16));

	tcpSocket->write(block);
	tcpSocket->flush();
}

void Client::onMessage( const QString &msg, const QVariant &data ) {
	if(msg == GD_RETR_WELCOME_STRING) {
		if(data.type() == QVariant::Map) {
			QMap<QString, QVariant> val;
			val = data.toMap();	
			for(QMap<QString, QVariant>::iterator it = val.begin();
				it != val.end(); ++it) {
				qDebug() << it.key() << ": " << it.value() << endl;
			}
		}
		readDir(this->_directory);
	}
	else {
		if(data.type() == QVariant::Map) {
			QMap<QString, QVariant> val;
			val = data.toMap();		
			for(QMap<QString, QVariant>::iterator it = val.begin();
				it != val.end(); ++it) {
				qDebug() << it.key() << ": " << it.value() << endl;
			}
		}
	}
}
Sobald etwas empfangen wird, wird dies in der Funktion onMessage() abgearbeitet. Wo soll ich den nun den transfer durchführen.

Den Transfer hätte ich wie folgt gemacht:

Code: Alles auswählen

	QFile *file = new QFile;
	file->setFileName(filename);
	//QFile file(this->qsFilename);
	if (!QFile::exists(file->fileName())) {
        cerr << QString("The file %1 does not exist.\n").arg(file->fileName()).toStdString() << endl;
        cerr << QString("%1").arg(file->errorString()).toStdString() << endl;
		cerr << QString("%1").arg(fileError(file->error())).toStdString() << endl;
        //return 4;FileFailure;
    }
    else if (!file->open(QIODevice::ReadOnly)) {
        //cerr << QString("Failed to open file %1.\n").arg(file.fileName()).toStdString() << endl;
        //cerr << QString("%1").arg(file.errorString()).toStdString() << endl;
		cerr << QString("%1: %2").arg(fileError(file->error())).arg(file->errorString()).toStdString() << endl;
        //cerr << QString("Failed to open file %1.\n").arg(file.fileName()).toStdString() << endl;
        //return FileFailure;
    }

		QFileInfo *file_info = new QFileInfo(file->fileName());


		qint64 file_size = file_info->size();
		qint64 bytes_read = 0;
		qint64 bytes = BUFFER_SIZE;
		qint64 bytes_to_read = bytes;
		qint64 max_bytes_to_transfer = TRANSFER_SIZE;
		max_bytes_for_all += file_size;

		do {
			bytes_to_read = ((bytes_read + bytes) >= file_size ? (file_size-bytes_read) : bytes);
			char *buffer = new char[bytes_to_read];
			memset(buffer, 0, bytes_to_read);
			qint64 ret = file->read(buffer, bytes_to_read);
			qint64 transfer_byte_count = 0;
			while(transfer_byte_count != bytes_to_read) {
				qint64 buffer_to_alloc = max_bytes_to_transfer;
				if((buffer_to_alloc + transfer_byte_count) > bytes_to_read)
					buffer_to_alloc = bytes_to_read - transfer_byte_count;

				char *tmp_buffer = new char[buffer_to_alloc];
				memset(tmp_buffer, 0, buffer_to_alloc);
				memmove(tmp_buffer, buffer + transfer_byte_count, buffer_to_alloc);

				QString filename_ = file_info->fileName();
				qint64 size = (bytes_read + buffer_to_alloc);
				qint64 offset = (bytes_read + transfer_byte_count);
				QByteArray bytearray = QByteArray::fromRawData(tmp_buffer, size);

				QCryptographicHash crypto_hash(QCryptographicHash::Md5);
				crypto_hash.addData(bytearray.constData(), size);

				QString md5sum = crypto_hash.result().toHex();

				QMap<QString, QVariant> val;
				val.insert(GD_FILE_MD5SUM, md5sum);
				val.insert(GD_FILE_NAME, filename_);
				val.insert(GD_FILE_OFFSET, offset);
				val.insert(GD_FILE_SIZE, size);
				//val.insert(GD_FILE_DATA, bytearray);

				emit sendMessage(GD_MSG_FILE_TRANSFER, val);
				/*char *buffer = new char[size];
				memset(buffer, 0, size);*/
				quint64 bytes_written = 0;
				do {
					/*quint64 ret = ::recv(tcpSocket->socketDescriptor(), buffer + bytes_read, size - bytes_read, 0);
					bytes_read += ret;*/
					quint64 ret = tcpSocket->write(tmp_buffer + bytes_written, size - bytes_written);
					bytes_written += ret;
				} while(bytes_written > size);
				delete [] tmp_buffer;
				tmp_buffer = NULL;

				transfer_byte_count += buffer_to_alloc;
			}
			delete [] buffer;
			buffer = NULL;

			bytes_read += bytes_to_read;
			/*cout << "Read " << percentage(file_size, bytes_read) << "% from file\r";// << endl;
			cout.flush();*/

		}while(bytes_read != file_size);	
		file->close();

		delete file_info;
		file_info = NULL;
		delete file;
		file = NULL;

		QueryPerformanceCounter(&ende_ticks); 
		tick_diff = ((double) ende_ticks.QuadPart - (double) start_ticks.QuadPart) / frequenz.QuadPart;
		if(i%1000 == 0)
			cout << i << "/" << TEST_LOOP << ".) " << "Summary bytes: " << max_bytes_for_all << " in " << tick_diff << " seconds" << endl;
		if(i == TEST_LOOP)
			break;
	}
Das problem ist nur das ich am server nicht tcpSocket->read() aufrufen kann, weil die funktion onReadyRead dauernd dazwischen funkt.

Lg NoRulez

P.S.: die 3 haupt funktionen onReadyRead(), sendMessage(), onMessage() sind auf dem client und server equivalent
Christian81
Beiträge: 7319
Registriert: 26. August 2004 14:11
Wohnort: Bremen
Kontaktdaten:

Beitrag von Christian81 »

Auf Signal readReady() warten und die Daten dort immer auslesen. Fertig.
MfG Christian

'Funktioniert nicht' ist keine Fehlerbeschreibung
NoRulez
Beiträge: 208
Registriert: 19. Juni 2007 15:43

Beitrag von NoRulez »

Kannst du mir bitte ein kurzes beispiel geben.
Der Workflow wie bisher sollte beibehalten werden. Lediglich der Dateitransfer soll hinzukommen.

Lg NoRulez
nando
Beiträge: 321
Registriert: 28. Oktober 2004 13:16

Beitrag von nando »

hmmm...
der client sendet einfach die datei nach dem er sich connected hat....
und sobald bytes beim server ankommen wird qt das signal bytesAvailable ausloesen und dann wird deine read methode aufgerufen (also die, die du mit dem signal bytesAvailable connected hast).
dann liest du alle verfuegbaren bytes und fertig.. kommen wieder bytes an, geht das spiel von vorne los...bis die datei uebertragen wurde.... musst halt schauen, wie du erkennst wann die uebertragung fertig ist...

gruss,
nando
NoRulez
Beiträge: 208
Registriert: 19. Juni 2007 15:43

Beitrag von NoRulez »

Für das Signal bytesAvailable gibt es bereits einen Slot. Mein Problem ist nur das dieser eben schon für die Kommunikation mit dem Server gebraucht wird (Siehe Eintrag 3 bei der Funktion:

Code: Alles auswählen

void Client::onReadyRead() 
)

Entweder stehe ich gerade am Schlauch oder sehe den Wald vor lauter Bäumen nicht ;-)

Im Prinzip weiß ich ja das ich es in der Methode onReadyRead() abhandeln müsste, nur weiß ich nicht ob gerade ein File transferiert wird oder eben eine Nachricht geschickt wird.

Lg NoRulez
PeterLustig
Beiträge: 386
Registriert: 21. November 2007 20:07

Beitrag von PeterLustig »

Dann markierst du deine Nachricht oder den Dateitransfer eben mit ein paar speziellen Extrabytes. Zum Beispiel muss zu Beginn jeder Nachricht das Keyword "MSG" stehen, oder bei jeder Datei "FILE".
Antworten