PDA

View Full Version : QTcp[server and socket]: can't read file sent



cerina
29th September 2012, 12:26
Good morning,
I’m looking for an example about sending a file from one pc to an other with QTcpSocket. I tried to create my own code. I have an application, in which, the user will choose a file from his DD ( all types) and send it to the TcpServer, this server will then send this file to the other clients.But, I have a problem, when i choose the file and i send it, in the client’s side, i have this message: file is sending , but in the server’s side, it shows me that the file isn’t recieved with it’s totaly bytes. Any suggestion please. This is the function for sending the file in the client’s side:

void FenClient::on_boutonEnvoyer_2_clicked()
{
QString nomFichier = lineEdit->text();
QFile file(lineEdit->text());
if(!file.open(QIODevice::ReadOnly))
{
qDebug() << "Error, file can't be opened successfully !";
return;

}

QByteArray bytes = file.readAll();

QByteArray block;
QDataStream out(&block, QIODevice::WriteOnly);

out << quint32(0);
out << nomFichier;
out << bytes;
out.device()->seek(0);
out << quint32((block.size() - sizeof(quint32)));

qDebug() << "Etat : envoi en cours...";
listeMessages->append("status : sending the file...");

socket->write(block);
}
and the server side:

void FenServeur::datarecieved()
{

QTcpSocket *socket = qobject_cast<QTcpSocket *>(sender());

if(socket == 0)
{
qDebug() << "no Socket!";
return;
}

forever
{
QDataStream in(socket);
if(blockSize == 0)
{
if(socket->bytesAvailable() )
{
qDebug() << "Error < sizeof(quint32))";
return;
}

in >> blockSize;
}

if(socket->bytesAvailable() < blockSize)
{
qDebug() << "data not recieved with its total bytes";

return;
}

qDebug() << "!!!!!!";
QByteArray dataOut;
QString nameFile;
in >> nameFile >> dataOut;
QFile fileOut(nameFile);
fileOut.open(QIODevice::WriteOnly);
fileOut.write(dataOut);
fileOut.close();

blockSize = 0;
}
}

void FenServeur::sendToAll(const QString &message)
{

QByteArray paquet;
QDataStream out(&paquet, QIODevice::WriteOnly);

out << (quint32) 0;
out << message;
out.device()->seek(0);
out << (quint32) (paquet.size() - sizeof(quint32));
for (int i = 0; i < clients.size(); i++)
{
clients[i]->write(paquet);
}

}
So i can't write the file that the server recieved into a new file. Any suggestion please!! and thanks in advance

wysota
29th September 2012, 17:47
You are making a common mistake of assuming that if you send X bytes in one chunk, those X bytes will also arrive to the destination in one chunk, which is not true.

The logic of your server side code is as follows:
1. Create a data stream object on the socket
2. If block size is not set, check if there is any data in the socket (1 byte is also "any"), if not then return.
3. Read the block size (note: point of failure since the block size can be more than one byte long)
4. if there is not enough data available, return to step 1.
5. read the name and content from the stream and write it to the file.

See the problem? By the way, the simplest possible algorithm of sending a file is:
1. Write four bytes in network byte order representing the size of the file name
2. Write the filename (e.g. UTF-8 encoded)
3. Write eight bytes in network byte order representing the length of the file
4. Until all the content is written to the socket, read a couple of bytes from the file and write them to the socket.

And reading end called upon receiving the readyRead() signal:
1. if there are less than 4 bytes available, return
2. read four bytes and interpret them as the file name length (let's call it FLEN), store the length
3. see if there are FLEN bytes available in the socket, if not return and next time start from (3)
4. read the file name, open the file for writing
5. see if there are eight bytes available in the socket -- if not, return and next time start from (5)
6. read the size of the file and store it in CONTENTSIZE
7. read up to CONTENTSIZE bytes from the socket, write them to the file and reduce CONTENTSIZE by the number of bytes read
8. if CONTENTSIZE > 0, return and next time start from (7)
9. close the file and report success

This way you are not using QDataStream and your algorithm can simply be implemented in environment without Qt, if needed. Another benefit is that this way you can transmit a file larger than the available size of your RAM.

Your implementation allows you to transmit only a file a bit shorter than half of available RAM (since you are keeping the file in memory twice -- once in "bytes" and once in "block").

cerina
29th September 2012, 22:37
thanks wysota for reply, I solved my problem using the code here: http://www.qtcentre.org/threads/48209-File-transfer-through-sockets?highlight=File+transfer+through+sockets

Thanks again for your clear response :)

cerina
30th September 2012, 13:27
I have just a little problem.It is that the received file is modified in its content. Following the reception, when I open the file, I found a line added to the beginning of the file, indicating the path to access the file from the sender (who sent file), how can I fix it not to add this line to the file in the reception??
Thanks

ChrisW67
1st October 2012, 01:32
I hope you didn't copy the other thread's generally unnecessary use of threads to do simple networking.

If you followed wysota's logic above then sender is sending the file name followed by the file content and the receiver is receiving the file name followed by the file content. You control the receiver and the sender. If you don't want the receiver to write the file name into the the file then don't code it to write every received byte into the file (because some of them will be length values and the file name). Alternatively, if you don't want to the original file name transmitted with the file content then don't send it.

cerina
6th October 2012, 19:13
i tried to follow wysota's logic, but i didn't know how i do in order to begin reading from just the content!!!!

wysota
7th October 2012, 16:42
Most trivial approach would be to use an 'if' statement that checks some particular flag and skips (or not) some piece of code. A more advanced approach would be to implement a simple state machine driving the communication logic.