salocinx
salocinx

Reputation: 3823

How to receive fragmented TCP data correctly?

I try to send a live video stream from a webcam over TCP. I compress the images with JPG before sending; that works fine. But I have some problem with the receiver socket. It somehow only get a quarter of the full images on the receiving side.

It looks like this after decoding the byte array I receive:

enter image description here

I have yet programmed the following code to receive the image data (receive runs within a thread):

    private void receive() {
        try {
            while(running) {
                if((available = clientSocket.Receive(buffer, 4, SocketFlags.None)) >= 0) {
                    int bytesToRead = BitConverter.ToInt32(buffer, 0);
                    using(MemoryStream ms = new MemoryStream(bytesToRead)) {
                        using(BinaryWriter bw = new BinaryWriter(ms)) {
                            while(bytesToRead > 0) {
                                if((available = clientSocket.Receive(buffer, getRemainingBytes(bytesToRead), SocketFlags.None)) >= 0) {
                                    bw.Write(buffer);
                                    bytesToRead -= available;
                                }
                            }
                            NetworkPacket.deserialize(ms.ToArray());
                        }
                    }
                }
            }
        } catch(Exception ex) {
            Logger.Log(Level.WARNING, "Could not receive data from TCP server.", ex);
        }
    }

    private int getRemainingBytes(int bytesToRead) {
        if(bytesToRead > clientSocket.ReceiveBufferSize) {
            return clientSocket.ReceiveBufferSize;
        } else {
            return bytesToRead;
        }
    }

As you can guess, the first 4 bytes (Int32) of my message structure defines the packet length I will have to read in for each image (these are between 30-40kB).

What happens behind the scene with the remaining data if I read only the first 4 bytes of the buffered data with clientSocket.Receive(buffer, 4, SocketFlags.None) ? Is it okay to read them in the second ´clientSocket.Receive(...)´ call or are the remaining bytes from the first call dropped when ´clientSocket.Receive(...)´ is called the second time?

How could I improve the receiver code under the assumption that about 30 images per second arrive each 30-40kB in size ?

Upvotes: 0

Views: 934

Answers (1)

Richard Schneider
Richard Schneider

Reputation: 35477

FIX

You should only "write" the bytes that you receive

Change bw.Write(buffer) to

bw.Write(buffer, 0, available);

Speedup

Instead of moving the received data between multiple buffers, you could directly read the network data into your buffer.

int bytesToRead = BitConverter.ToInt32(buffer, 0);
int offset = 0;
byte[] frame = new byte[bytesToRead];
while (bytesToRead > 0)
{
    var n = clientSocket.Receive(frame, offset, bytesToRead);
    offset += n;
    bytesToRead -= n;
}

NetworkPacket.deserialize(frame);

Upvotes: 2

Related Questions