views:

454

answers:

2

I have code where I am trying to grow the byte array while receiving the data over my socket. This is erroring out.

    public bool ReceiveObject2(ref Object objRec, ref string sErrMsg)
    {
        try
        {
            byte[] buffer = new byte[1024];
            byte[] byArrAll = new byte[0];
            bool bAllBytesRead = false;

            int iRecLoop = 0;

            // grow the byte array to match the size of the object, so we can put whatever we 
            // like through the socket as long as the object serialises and is binary formatted 
            while (!bAllBytesRead)
            {
                if (m_socClient.Receive(buffer) > 0)
                {
                    byArrAll = Combine(byArrAll, buffer);
                    iRecLoop++;
                }
                else
                {
                    m_socClient.Close();
                    bAllBytesRead = true;
                }
            }

            MemoryStream ms = new MemoryStream(buffer);
            BinaryFormatter bf1 = new BinaryFormatter();
            ms.Position = 0;
            Object obj = bf1.Deserialize(ms);
            objRec = obj;

            return true;
        }
        catch (System.Runtime.Serialization.SerializationException se)
        {
            objRec = null;
            sErrMsg += "SocketClient.ReceiveObject " + "Source " + se.Source + "Error : " + se.Message;
            return false;
        }
        catch (Exception e)
        {
            objRec = null;
            sErrMsg += "SocketClient.ReceiveObject " + "Source " + e.Source + "Error : " + e.Message;
            return false;
        }
    }

    private byte[] Combine(byte[] first, byte[] second)
    {
        byte[] ret = new byte[first.Length + second.Length];
        Buffer.BlockCopy(first, 0, ret, 0, first.Length);
        Buffer.BlockCopy(second, 0, ret, first.Length, second.Length);
        return ret;
    }    

Error: mscorlibError : The input stream is not a valid binary format. The starting contents (in bytes) are: 68-61-73-43-68-61-6E-67-65-73-3D-22-69-6E-73-65-72 ...

Yet when I just cheat and use a MASSIVE buffer size its fine.

        public bool ReceiveObject(ref Object objRec, ref string sErrMsg)
    {
        try
        {
            byte[] buffer = new byte[5000000];

            m_socClient.Receive(buffer);
            MemoryStream ms = new MemoryStream(buffer);
            BinaryFormatter bf1 = new BinaryFormatter();

            ms.Position = 0;
            Object obj = bf1.Deserialize(ms);
            objRec = obj;

            return true;
        }
        catch (Exception e)
        {
            objRec = null;
            sErrMsg += "SocketClient.ReceiveObject " + "Source " + e.Source + "Error : " + e.Message;
            return false;
        }
    }

This is really killing me. I don't know why its not working. I have lifted the Combine from a suggestion on here too, so I am pretty sure this is not doing the wrong thing?

I hope someone can point out where I am going wrong

+1  A: 

I'm not really familiar with C# networking, but aren't you appending full 1024 bytes to the buffer every time you call Combine() and ignoring the number of bytes read off the socket? You probably need at least one extra parameter to that function telling how many bytes to copy from second.

Nikolai N Fetissov
@Nikolai buffer is 1024 bytes fixed and I *should* be adding this to the byArrAll. So the massive buffer obv has wasted space on the end of it, as will the combined array. Just that the combined array shoudl be at max 1023 too big if you catch my drift?
FinancialRadDeveloper
You should be adding number of bytes read from the socket, which is less or equal to 1024 here.
Nikolai N Fetissov
+2  A: 

That Combine method is a really expensive way to grow an array, especially when MemoryStream is designed to address this; and the other replies are right: you must check the number of bytes read:

using(MemoryStream ms = new MemoryStream()) {
    int bytesRead;
    while((bytesRead = m_socClient.Receive(buffer)) > 0) {
        ms.Write(buffer, 0, bytesRead);
    }
    // access ms.ToArray() or ms.GetBuffer() as desired, or
    // set Position to 0 and read
}

Of course, you could just read directly from the stream (pass it to your reader)

Also - if your serialization is too big, you might consider alternative encoders, such as protobuf-net (although this will change the code a little). This may fix the problem with a huge object.

Marc Gravell
Gaargh! I thought this would be the answer. Sadly it now reads in the loop and hangs when there are no more bytes left. I don't know why this is happening because I thought it would just return zero and then exit.
FinancialRadDeveloper
I think I will need to check bytes read, and if this is smaller than the buffer than that exits the loop as getting down to the end it is hanging. I guess its waiting for another send to happen as its finished the first receieve and if you were to call another then you would be expecting another load of data.
FinancialRadDeveloper
@AlanR - in that case, check that the remote stream has been closed. If the intent is to leave it open, then yes: you'll need to track your own progress through a set amount of data.
Marc Gravell
I am an idiot, I commented out the Close(); late last night and so thats why it was hanging. It works well now. I really didn't like the sending of a datasize approach. Thanks. If I had ANY rep I would try to give you some for your help ;P
FinancialRadDeveloper