0

I'm writing a program to send files between a client and a server through local network in C++ with Qt_5.9 and the transfer stops randomly (sometimes at ~400MB, but sometimes at way less).

Here is the method writing the file content on the socket (in servertcp.cpp) :

void ServerTcp::write_file(QString fileName){
    QFile file(fileName);
    if(file.open(QIODevice::ReadOnly)){
        qint64 nb = 0;
        while(nb < file.size()){
            QByteArray partial = file.read(65536);
            clientConnection->write(partial, partial.count());
            // I think I have to write something here
            qint64 nbb = partial.count();
            nb += nbb;
            qDebug() << nbb << " (" << nb << ")";
        }

        file.close();

        clientConnection->write("[[[END]]]");
    }
}

And here is the method reading (called when readyRead is emitted) the socket (in clienttcp.cpp) :

void ClientTcp::read()
{
    qDebug() << "To read : " << soc.size();

    emit to_IHM_text(soc.readAll());
}

The problem I face is that at some point, the client stops reading the socket (meaning we don't enter in the read method anymore, so I guess readyRead isn't emitted), but the server still writes in it (and in the task manager I can see the memory use increasing).

Can anyone tell me what I'm doing wrong ? (apart from using TCP for file transfer when FTP exists) I tried inserting clientConnection->waitForReadyRead(X) in the write_file method, but it always waits X ms regardless the socket state.


Edit:

So I updated my write_file method to check the connection state :

void ServerTcp::write_file(QString fileName){
    QFile file(fileName);
    if(file.open(QIODevice::ReadOnly)){
        qint64 nb = 0;
        while(nb < file.size()){
            QByteArray partial = file.read(65536);

            if(clientConnection->bytesToWrite()>0){
                problem = true;
                qDebug() << clientConnection->bytesToWrite();
                clientConnection->waitForBytesWritten();
            }

            qDebug() << clientConnection->state();
            qint64 nbb = clientConnection->write(partial, partial.count());
            nb += nbb;
            qDebug() << nbb << " (" << nb << ")";
        }

        file.close();

        clientConnection->write("[[[END]]]");
    }
}

And it always prompts QAbstractSocket::ConnectedState during all the file transfer, meaning the problem is elsewhere.

Apparently, even if clientConnection->write(...) always returns 65536 (except for last chunk of the file), the socket randomly refuses to effectively write the bytes (as clientConnection->bytesToWrite() returns multiples of 65536 after a while).

Concerning the way the client knows it received the whole file, I use a homemade handshake with a header sent apart (file name + size) and a response (ok to receive or not ok).


Edit 2 :

I tried another way (connecting the socket bytesWritten signal to a method which only sends a small part of the file), and same applies : at some point, it seems that congestion occurs...

Rafalon
  • 4,450
  • 2
  • 16
  • 30
  • 2
    From what I can see you are not checking if the connection is active or not. – macroland Aug 30 '17 at 07:07
  • @macroland It obviously is at least at the beginning since the transfer works, but then how can I ensure it stays active ? – Rafalon Aug 30 '17 at 07:10
  • You need to Make a `keepAlive` for yourself between server/client for example every 500ms. – Farhad Aug 30 '17 at 07:11
  • can you explain this part: ` I can see the memory use increasing` it should be socket buffer, not memory(RAM) – Farhad Aug 30 '17 at 07:12
  • before you read data it's socket buffer (a limit memory) after you read data it's your decision to keep it (increase memory using) or delete data. https://stackoverflow.com/questions/12931528/c-socket-programming-max-size-of-tcp-ip-socket-buffer – Farhad Aug 30 '17 at 07:46
  • And what client is doing if temporary there is no bytes in the incoming buffer? Goes it to short sleep to allow the buffer to be refilled? Or just assumes there is no more data and stops reading from socket? Also - How does Client know how many bytes is expected? – Artemy Vysotsky Aug 30 '17 at 07:51
  • 1
    @Rafalon Please read this: http://doc.qt.io/qt-5/qabstractsocket.html Focus on functions : `setReadBufferSize(qint64 size)` or `qint64 readBufferSize() const` – Farhad Aug 30 '17 at 07:54
  • @ArtemyVysotsky client only reads the incoming buffer when `readyRead` is emitted – Rafalon Aug 30 '17 at 08:32
  • https://stackoverflow.com/questions/25634483/send-binary-file-over-tcp-ip-connection – macroland Aug 30 '17 at 09:17

1 Answers1

0

From your revised code:

   while(nb < file.size()){
        QByteArray partial = file.read(65536);

        [...]

        qDebug() << clientConnection->state();
        qint64 nbb = clientConnection->write(partial, partial.count());
        nb += nbb;
    }

Have you gamed out what happens when write() returns a smaller value than partial.count(), above?

If not, I'll give you an example. Say write() return 32000 rather than 65536. That mean the first 32000 bytes of your 65536-byte QByteArray went out to the TCP socket; what happens to the remaining 33536 bytes? Nothing, that's what -- they simply get ignored and never go out to the client at all. Therefore the data the client receives will only be part of the data the original file contained, because the data-stream is missing arbitrary chunks from various places in the middle of the file. That's probably not the behavior you want.

The fix to handle the short-write() problem would be to call write() again with just the remaining 32000 bytes of data (and repeat as necessary until all of the bytes in the partial-buffer have actually been handed off to the socket-buffer, as indicated by the results of the write() calls). Then, and only then, is it time to read more data from the file to send.

Jeremy Friesner
  • 70,199
  • 15
  • 131
  • 234
  • Interesting but in debug it always returned 65536 except for the very last chunk, so the bug doesn't come from here, even if it could have – Rafalon Aug 30 '17 at 20:40
  • Also, I tried transferring video files and I'm quite sure that chunks are received in the right order (as I can play the movie until like 40mn or so) – Rafalon Aug 30 '17 at 20:50