Seite 1 von 1

Binärdatei übertragen + QTcpSocket

Verfasst: 27. März 2008 22:35
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

Verfasst: 28. März 2008 08:24
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

Verfasst: 28. März 2008 09:06
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

Verfasst: 28. März 2008 09:24
von Christian81
Auf Signal readReady() warten und die Daten dort immer auslesen. Fertig.

Verfasst: 28. März 2008 11:12
von NoRulez
Kannst du mir bitte ein kurzes beispiel geben.
Der Workflow wie bisher sollte beibehalten werden. Lediglich der Dateitransfer soll hinzukommen.

Lg NoRulez

Verfasst: 28. März 2008 12:41
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

Verfasst: 1. April 2008 20:46
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

Verfasst: 1. April 2008 22:10
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".