Click here to Skip to main content
16,020,182 members
Please Sign up or sign in to vote.
0.00/5 (No votes)
In my C# code I am receiving a byte[1024] from a DLL through TCP link. Code I did for this I am attaching below.

The workflow is

An asynchronous packet gets received at tcp port into a byte array, which then gets copied to a different array for processing.

This copied array then gets processed according to received packet size(each message could be of different size one behind another).

While in the meantime async method received another data which should get appended to array used for processing packet.
Problem coming is being method as asynchronous data arriving at TCP port is getting appended anytime to the processing buffer array which many times throws exceptions while its size is getting referred during Copy methods. I am unable to block this async call as after processing one packet.

C#
/* Method to process recieved packet  */
_message.OnRecieve(message);


UI is getting updated and on user's action again the send and receive methods getting called where receive buffer array gets overwritten with new data.

Sharing piece of code:

What I have tried:

C#
public class Client
{
    // State object for receiving data from remote device.
    public class StateObject
    {
        // Client socket.
        public Socket workSocket = null;
        // Size of receive buffer.
        public const int BufferSize = 1024;
        // Receive buffer.
        public byte[] buffer = new byte[1024];   
    }

    public static T ByteArrayToStructure<T>(byte[] bytes) where T : struct
    {
        var handle = GCHandle.Alloc(bytes, GCHandleType.Pinned);
        try
        {
            return (T)Marshal.PtrToStructure(handle.AddrOfPinnedObject(), typeof(T));
        }
        finally
        {
            handle.Free();
        }
    }

    [StructLayout(LayoutKind.Sequential,Pack = 2)]
    public struct MessageHeader
    {
        public int size;
    }

    private static byte[] data = new byte[1024 * 10];

    private static void ReceiveCallback(IAsyncResult ar)
    {
        try
        {
            // Retrieve the state object and the client socket 
            // from the asynchronous state object.
            StateObject state = (StateObject)ar.AsyncState;
            Socket client = state.workSocket;

            // Read data from the remote device.
            int bytesRead = client.EndReceive(ar);

            if (bytesRead > 3)
            {
                //if data does not contain any data to process 
                if (data.Length == 0)
                    Array.Copy(state.buffer, 0, data, 0, bytesRead);
                else
                {
                    //resize data array according to data it contains+data arrived
                    Array.Resize(ref data, data.Length + bytesRead);
                    //if data array contains data and state.buffer arrives with new data 
                    Array.Copy(state.buffer, 0, data, data.Length, bytesRead);
                }

                // process all data that exist in data array  
                while (data.Length > 2)
                {
                    byte[] headerbyte = new byte[2];

                    //read two byes of data contains message length in format IPAddress.NetworkToHostOrder
                    Array.Copy(data, 0, headerbyte, 0, 2);

                    //reverse bytes in headerbyte
                    Array.Reverse(headerbyte);

                    Array.Copy(headerbyte, 0, data, 1, 1);
                    Array.Copy(headerbyte, 1, data, 0, 1);

                    //getting recieved message size from structure
                    MessageHeader header = ByteArrayToStructure<MessageHeader>(data);

                    int packetSize = header.size;

                    //if data contains within data array is partial packet
                    if (data.Length < packetSize)
                    {
                        // Get the rest of the data.
                        client.BeginReceive(state.buffer, 0, StateObject.BufferSize, 0,
                            new AsyncCallback(ReceiveCallback), state);
                    }
                    else
                    {
                        byte[] message = new byte[packetSize];
                        byte[] remainingData = new byte[data.Length - packetSize];

                        //copy message data to process into message array
                        Array.Copy(data, 0, message, 0, packetSize);
                        //copy remainng data into a temp array
                        Array.Copy(data, packetSize, remainingData, 0, data.Length - packetSize);

                        //Method to process recieved packet
                        _message.OnRecieve(message);

                        //Removing processed Message from data array by resizing it to of size remaingin data and copying content into it
                        Array.Resize(ref data, remainingData.Length);
                        Array.Copy(remainingData, 0, data, 0, remainingData.Length);
                    }
                }
                // Get the rest of the data.
                client.BeginReceive(state.buffer, 0, StateObject.BufferSize, 0,
                    new AsyncCallback(ReceiveCallback), state);
            }
            else
            {
                // Create call back for next incoming packet
                client.BeginReceive(state.buffer, 0, StateObject.BufferSize, 0,
                    new AsyncCallback(ReceiveCallback), state);
            }
        }
        catch (Exception e)
        {
            Console.WriteLine(e.ToString());
        }
    }
}
Posted
Updated 18-Mar-18 23:09pm
v2

1 solution

You are (effectively) using a static data buffer for your input messages, so any time some data arrives it has the potential to destroy what is already there. You should allocate your buffer in the async event handler, capture the data, and pass the buffer off to the method that will process it. Then if more data arrives you will create a new buffer rather than overwriting what is still being processed.

Also you have the following line in your code:
C#
public const int BufferSize = 1024;

but you still use th hard coded value 1024 everywhere. You should use the constant name like:
C#
// Receive buffer.
public byte[] buffer = new byte[BufferSize];

So if you ever change from 1024, you will not need to search the code for other places where that number is used.
 
Share this answer
 

This content, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)



CodeProject, 20 Bay Street, 11th Floor Toronto, Ontario, Canada M5J 2N8 +1 (416) 849-8900