0

I need to transfer an audio files byte by byte in socket and in receiver side I need to arrange it properly. For example, there is two audio files A and B, I have to read one byte from A and transfer, next I have to read first byte from B and transfer, then second byte from A and second byte from B like this the transfer has to be done till the EOF, in receiver side I have to organize properly received bytes for A and B.

The above one I tried in C++(Qt) in TCP sockets for one audio file.

QString path = "a4.wav";
QFile inputFile(path);
inputFile.open(QIODevice::ReadOnly);

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

QByteArray q = inputFile.readAll();
block.append(q);
inputFile.close();

out.device()->seek(0);

qint32 x = 0;
unsigned int i = 0;
while (x < block.size()) {
    qint32 y = socket->write(&block.data()[i],1);   //Filling one byte in socket to write
    x += y;
    //qDebug() << y;
    //qDebug() << x;    // summary size you send, so you can check recieved and replied sizes
    i++;
}

But here in above code I'm filling and writing one byte, but receiver side I'm getting bunch of bytes(for example receiving 14000bytes). It seems like the buffer in write socket fills maximum and then only transfer is happening. What I have to do, to write only one byte at a time?

paulraj J
  • 65
  • 6
  • Why are you reading the file into byte array q then copying it to block? Why are you opening the DataStream 'out'? – john elemans Aug 01 '17 at 05:49
  • Nobody does TCP one byte per packet. TCP is a stream, so network equipment can slice and glue it however it wants. May work with UDP, but still waste because for each byte you also send packet headers that are 20 to 40 bytes long. – Velkan Aug 01 '17 at 05:57

1 Answers1

2

You shouldn't do i/o based on byte by byte logic, it's not efficient. It also might enable the so called Nagle algorithm which will wait for more data before sending the packet. What i recommend on the sending side:

 QByte * p = buffer.data();
 int sizeLeft = static_cast<int>(buffer.size());
 while (sizeLeft > 0) {
      int n = socket.send(p, sizeLeft);
      if (n < 0) {
           // Error
           return false; // or throw exception 
      }
      p += n;
      sizeLeft -= n;
 }

On the receiving size you should have a decent buffer so you could do the same buffering. The example is with std vector but it may apply to Qt containers as well

int appendReceived(QSocket &socket, std::vector<std::uint32_t> buffer, int maxRecvLength) {
     size_t prevSize = buffer.size();
     buffer.resize(prevSize + maxRecvLength);
     int n = socket.recv(buffer.data() + prevSize, maxRecvLength);
     if (n < 0) {
         // Error
         return n; // or throw exception 
     }
     buffer.resize(prevSize + n);
}

You can maybe do the arrangements on the sending side before starting to write the data.

std::vector<std::uint8_t> mergeVectors(const std::vector<std::uint8_t> &a, const std::vector<std::uint8_t> &b) {
    std::vector<std::uint8_t> result;
    result.reserve(a.size() + b.size()));
    size_t minSize = std::min(a.size(), b.size());
    for(size_t i = 0; i < minSize; ++i) {
        result.push_back(a[i]);
        result.push_back(b[i]);
    }
    for (size_t i = minSize; i < a.size(); ++i) {
        result.push_back(a[i]);
    }
    for (size_t i = minSize; i < b.size(); ++i) {
        result.push_back(b[i]);
    }
    return result;
}

On the receiving side you may have:

void splitAndRemoveFront(std::vector<std::uint8_t> &buffer, std::vector<std::uint8_t> &a, std::vector<std::uint8_t> &b, size_t asize, size_t bsize) {
    assert(buffer.size() >= asize + bsize);
    size_t minSize = std::min(asize, bsize);
    a.reserve(a.size() + asize);
    b.reserve(b.size() + bsize);
    auto p = buffer.begin();
    for(size_t i = 0; i < minSize; ++i) {
        a.push_back(*p++);
        b.push_back(*p++);
    }
    for (size_t i = minSize; i < asize; ++i) {
        a.push_back(*p++);
    }
    for (size_t i = minSize; i < bsize; ++i) {
        b.push_back(*p++);
    }
    buffer.erase(buffer.begin(), p);
}

of course you will also need to transfer the size of the files somehow but you get the basic idea.