Share via

How handle text and binary data in "ReceiveCallback"?

To duro 61 Reputation points
2023-08-19T23:52:51.53+00:00

I had mirrored in @andrew code's in this question:

Cause a connected socket to accept new messages right after .BeginReceive?

to implement a more efficient version where we can receive not only text, but also binary data (like images).

In this moment i'm working in a android > C# (windows) connection (already handle big endian) where after some changes, the referred code seems make the promissed in that moment; but when i try receive a PNG image (remote screen) the header size not is received first and the data jumps immediately to "second" code block > if ((cDataLength + dataRead) >= c.DataSize){}.

Only after it, image size is received on "first" code block, but with very strange number for example:

  • 167,45 KB expected (sent correctly by android)
  • 41,9 MB received

Seems that to binary data not is necessary handle endianness because the first chunk of image is good, but the nexts are strange data and program starts to fail; even after remove the code related to endianness.

Then, why this is happennig and how fix?

Here is all relevant code:

class Client
{
    public Socket socket;
    public string RemoteAddress = "";
    public byte[] Buffer = new byte[1024];
    public int DataSize = 0;
    public int PrefixSize = 0;
    public bool DataSizeReceived = false;
    public bool lengthRead = false;
    public MemoryStream Data = new MemoryStream();

    public Client(Socket socket)
    {
        this.socket = socket;
        RemoteAddress = socket.RemoteEndPoint.ToString();
    }
}
    
private void ReceiveCallback(IAsyncResult ar)
{
    int dataOffset = 0;
    int restOfData = 0;
    int dataRead = 0;
    Boolean StreamClosed = false;
    long cDataLength = 0;
    long LastcDataLength = 0;

    SocketError socketError = SocketError.TypeNotFound;

    Client c = (Client)ar.AsyncState;

    if (!c.socket.Connected)
    {
        // Not Connected anymore ?
        return;
    }

    try
    {
        dataRead = c.socket.EndReceive(ar, out socketError);
    }
    catch (Exception)
    {
        // Your code goes here..
        return;
    }

    if (socketError != SocketError.Success)
    {
        // Has connection been lost ?
        return;
    }

    if (dataRead <= 0)
    {
        // Has connection been lost ?
        return;
    }

    while (dataRead > 0)
    {
        if (!c.DataSizeReceived)
        {
            if (!c.lengthRead)
            {
                byte[] BodyLengthR = new byte[4];
                Array.Copy(c.Buffer, BodyLengthR, 4);
                Int32 Endian = BitConverter.ToInt32(BodyLengthR, 0);
                Int32 BodySize = BinaryPrimitives.ReverseEndianness(Endian);

                c.PrefixSize = BodySize + 4;
                Console.WriteLine("PrefixSize = " + Utils.SizeSuffix(c.PrefixSize));
                Array.Resize(ref c.Buffer, c.PrefixSize);
                c.lengthRead = true;
            }

            if (c.Data.Length > 0)
            {
                restOfData = c.PrefixSize - (int)c.Data.Length;
                c.Data.Write(c.Buffer, dataOffset, restOfData);
                dataRead -= restOfData;
                dataOffset += restOfData;
            }
            else if (dataRead >= c.PrefixSize)
            {
                c.Data.Write(c.Buffer, dataOffset, c.PrefixSize);
                dataRead -= c.PrefixSize;
                dataOffset += c.PrefixSize;
            }
            else
            {
                c.Data.Write(c.Buffer, dataOffset, dataRead);
                dataOffset += dataRead;
                dataRead = 0;
            }

            if (c.Data.Length == c.PrefixSize)
            {
                c.DataSize = c.PrefixSize;
                c.DataSizeReceived = true;
                c.Data.Position = 0;
                c.lengthRead = false;

                //===========================================================================

                string textRecv = Encoding.UTF8.GetString(c.Data.GetBuffer(), 0, c.DataSize);

                if (textRecv.Contains("TEXT"))
                {
                    // Handle the received messsage
                }

                if (textRecv.Contains("IMAGE"))
                {
                    // Handle the received messsage
                }

                //==========================================================================

                c.Data.SetLength(0);
            }
            else
            {
                c.socket.BeginReceive(c.Buffer, 0, c.Buffer.Length, SocketFlags.None, new AsyncCallback(ReceiveCallback), c);
                return;
            }
        }

        try
        {
            cDataLength = c.Data.Length;
            LastcDataLength = cDataLength;
        }
        catch (ObjectDisposedException)
        {
            StreamClosed = true;
        }

        if (!StreamClosed)
        {
            if ((cDataLength + dataRead) >= c.DataSize)
            {
                restOfData = c.DataSize - (int)c.Data.Length;

                c.Data.Write(c.Buffer, dataOffset, restOfData);

                // Handle the received messsage

                //===========================================================================

                string textRecv = Encoding.UTF8.GetString(c.Data.GetBuffer(), 0, (int)c.Data.Length);

                if (textRecv.Contains("TEXT"))
                {
                    // Handle the received messsage
                }

                if (textRecv.Contains("IMAGE"))
                {
                    int index = textRecv.IndexOf("IMAGE");
                    byte[] bytes = Utils.RemoveAt(c.Data.ToArray(), 0, index); // index + 5 (save to file)
                    
                    /*
                    string Recv = Encoding.UTF8.GetString(bytes, 0, bytes.Length);
                    Console.WriteLine(Recv);
                    Image img = (Bitmap)((new ImageConverter()).ConvertFrom(bytes));
                    var img2 = new Bitmap(img);
                    img2.Save("C:\\Users\\Eu\\Desktop\\Test.png", ImageFormat.Png);
                    */
                }

                //==========================================================================

                dataOffset += restOfData;
                dataRead -= restOfData;

                c.Data = new MemoryStream();
                c.Data.Position = 0;
                c.DataSizeReceived = false;
                c.DataSize = 0;

                if (dataRead == 0)
                {
                    if (_IsConnected(c.socket))
                    {
                        c.socket.BeginReceive(c.Buffer, 0, c.Buffer.Length, SocketFlags.None, new AsyncCallback(ReceiveCallback), c);
                        return;
                    }
                }
                else
                    continue;
            }
            else
            {
                if (_IsConnected(c.socket))
                {
                    c.Data.Write(c.Buffer, dataOffset, dataRead);

                    c.socket.BeginReceive(c.Buffer, 0, c.Buffer.Length, SocketFlags.None, new AsyncCallback(ReceiveCallback), c);

                    dataRead = 0;
                }
            }
        }
        else
        {
            if (LastcDataLength + dataRead == c.DataSize)
            {
                if (_IsConnected(c.socket))
                {
                    c.socket.BeginReceive(c.Buffer, 0, c.Buffer.Length, SocketFlags.None, new AsyncCallback(ReceiveCallback), c);
                }
                return;
            }
            else
            {

            }
        }

        if (!_IsConnected(c.socket))
            dataRead = -1;
    }
}

private bool _IsConnected(Socket socket)
{
    try
    {
        return !(socket.Poll(1000, SelectMode.SelectRead) && socket.Available == 0);
    }
    catch (SocketException)
    {
        return false;
    }
}
Developer technologies | C#
Developer technologies | C#

An object-oriented and type-safe programming language that has its roots in the C family of languages and includes support for component-oriented programming.


Your answer

Answers can be marked as 'Accepted' by the question author and 'Recommended' by moderators, which helps users know the answer solved the author's problem.