Search code examples
c#arraystcpmonotransmission

Why does TCP client not send all bytes?


I use a TCP socket via localhost to pass data between my C#/Mono program and a Python script, on Raspbian. The data is about 64k bytes. The problem is that not all bytes are sent. The maximum number of bytes sent is 49152. I expect them to arrive in a single packet.

My code:

tcpListener = new TcpListener(ip, port);
tcpListener.Start();
tcpClient = tcpListener.AcceptTcpClient();
tcpClient.SendBufferSize = 1000000;
tcpClient.ReceiveBufferSize = 1000000;
networkStream = tcpClient.GetStream();

I've tried both of the following methods to write data, but neither send all bytes.

tcpClient.Client.Send(byteArray, byteArray.Length, SocketFlags.None);
// OR            
while (!networkStream.CanWrite)
{
     Thread.Sleep(50);
}
networkStream.Write(byteArray, 0, size);

Currently I use two successive transmissions to send the data to the script.

Is this a Mono bug? Or am I doing something wrong here?


Solution

  • I have encountered this issue before. Basically, you need to take into consideration how network operations actually work.

    When you do

    networkStream.Write(byteArray, 0, size);
    

    Yes, you are writing it to the stream, but the stream needs to remain open until the client has finished receiving the data.

    In my case- I was sending data, then assuming that the networkStream.Read operation had finished, and then closing the connection on the 'Write' side.

    If the client doing the writing closes the connection, after they 'think' the file/data has been wrote, but before the receiver has actually 'read' the data, then you can run into instances where parts of the data are lost.

    The way to solve this is to create a protocol with your networking.

    A common strategy is to measure the amount of data you wish to send and then send that ahead of the payload as a long variable. The receiver will then know how much data is expected and will read that amount of data from the stream. Once finished, the receiver should let the client know that they have received all the data.

    The writing client can be 'waiting' for this acknowledgement message in order to keep the stream awake.

    One of the earlier solutions I used was :

    while (client.IsConnected())
           {
                System.Threading.Thread.Sleep(100);
           }
    

    Right after sending a large file. Then I rely on the receiver to terminate the connection themselves. If the connection is terminated, it would,in my case, be the signal I needed to know that the file had finished transmitting.