I am trying to send a large file via UDP protocol . But the fact is that through UDP you cannot send files more than 8k bytes.

How can I split my file, which is put into QByteArray into smaller files and sent by several datagrams to the server?

Client code:

 void client::sendDatagrams(){ QByteArray data; QFile* file = new QFile("test"); file->open(QFile::ReadOnly); QString hash_summ = QString(QCryptographicHash::hash(data,QCryptographicHash::Md5).toHex()); qDebug() << "Hash: " << hash_summ; data = file->readAll(); qDebug() << "File size: " << data.size(); qDebug() <<"Text: " <<data; for(int i = 0; i < data.size(); i++){ Client_Socket->writeDatagram(data , QHostAddress::LocalHost,1234); } } 
  • The documentation did not find any mention of limiting the size of the sent datagrams. Correct, if something I do not know. - aleks.andr
  • UDP use is fundamental? In general, this task is more suitable for TCP. - Cerbo
  • @ aleks.andr, yes, there is . - alexis031182
  • @ alexis031182 Allow me, it says here: Sending datagrams larger than 512 bytes is in general disadvised, as even if they are sent successfully, they are likely to be fragmented by the IP layer before arriving at their final destination. Those. sending larger datagrams is not explicitly prohibited, it only warns that they will be fragmented. - aleks.andr
  • @ aleks.andr, you ignored the paragraph above the quoted text on the link I provided. - alexis031182

1 answer 1

It is possible that this solution will do:

 void client::sendDatagrams() { QFile file("test.dat"); if(file.open(QFile::ReadOnly)) { // Размер прочитанных из файла данных // и текущее смещение от начала файла. qint64 raw_size = 0, raw_offset = 0; // Константный размер датаграммы. const qint64 datagram_size = 512; // Буфер для временного хранения данных. char raw_data[datagram_size]; // Файл читаем последовательно и ровно столько байт за итерацию, // сколько может быть размещено в буфере. while((raw_size = file.readData(raw_data, datagram_size)) > 0) { // Метод fromRawData() не производит копирование данных. QByteArray data = QByteArray::fromRawData(raw_data, raw_size); // Вычисление хэш-суммы. QByteArray hash = QCryptographicHash::hash(data , QCryptographicHash::Md5).toHex(); // Итоговая датаграмма. QByteArray datagram; // Заполнять её лучше через QDataStream, // поскольку на приёмной стороне будет проще // произвести обратные действия, // чтобы восстановить исходные данные. // Смещение (raw_offset) сохраняем для того, // чтобы на приёмной стороне иметь возможность // воспроизвести порядок следования датаграмм, // т.к. при использовании UDP, последние могут // приходить без соблюдения очерёдности. QDataStream stream(&datagram, QIODevice::WriteOnly); stream << raw_offset; stream << hash; stream << data; Client_Socket->writeDatagram(datagram , QHostAddress::LocalHost, 1234); // Обновление смещения. raw_offset += raw_size; } } } 

Added by

The implementation of the receiver may differ from the code in the listing below, depending on the specific task. But suppose you just need to save the resulting file contents.

Of course, before we can write data to a file, it needs to be prepared. For this, the size of the file must be received from the sender before sending the contents of the file. In the source above, this preparatory moment is not considered, since its implementation is virtually no different from transferring the contents of the file, and depends on the preferences of the author of the code.

Suppose that we have already received the size of the file being sent to the receiver, or we know it in advance. Then we create a clean file and change its size exactly as required:

 // Файл будет заполнен нулями. QFile file("test-out.dat"); file.resize(file_size); 

Then you can take content:

 void receiver::readPendingDatagrams() { while(socket->hasPendingDatagrams()) { QByteArray datagram; datagram.resize(socket->pendingDatagramSize()); socket->readDatagram(datagram.data(), datagram.size()); // При считывании данных из потока важно соблюдать // их очерёдность и тип. // Можно не опасаться того, что hash и data // имеют одинаковый тип, т.к. QDataStream // корректно обработает данную ситуацию. QDataStream stream(datagram); qint64 offset = 0; stream >> offset; QByteArray hash; stream >> hash; QByteArray data; stream >> data; // Проверяем хэш-сумму. if(QCryptographicHash::hash(data , QCryptographicHash::Md5).toHex() != hash) { // В случае несовпадения реагируем, // исходя из требований задачи. continue; } // Сохраняем данные, учитывая смещение. if(file.open(QFile::WriteOnly)) { QDataStream stream(&file, QIODevice::WriteOnly); if(stream.skipRawData(offset) == offset) { stream << data; } file.close(); } } } 

Added by

If you are not sure that the control datagram containing the file size is safe and sound, then, alternatively, this data can be sent via TCP / IP. It will turn out a mechanism similar in some way to torrents.

  • There is still interesting how to collect the file on the receiver. Maybe add to the answer? - user3195373
  • Probably at the very end you need to send another datagram (that's all, the end of the data). Or to provide such a sign in the format of all the datagram .. But how else would the receiver know that it had already read the entire file? - avp
  • @ user3195373, added. - alexis031182
  • @avp, can be different, but I would just send the size of the data in the first packet. The code will be similar to sending the contents of the data. Well, or not to bother, really, you can simply send the size of the entire file in each datagram. - alexis031182
  • one
    @avp, I agree with you. Plus, it turns out that there is still no escape from the control requests, because a part of the datagrams, for example, may not reach. In this case, you need some service request through the same tcp to re-send part of the data. But with a video stream, probably, one could not give a damn about the possible percentage of losses, exposing the missing pixels averaged values ​​calculated from those that were not lost. So they probably do. - alexis031182