honeyashu Ответов: 1

Невозможно создать динамический байт[] для обработки данных, полученных через асинхронный ответ TCP-сокета


В моем коде C# я получаю байт[1024] из библиотеки DLL по TCP-ссылке. Код, который я сделал для этого, я прилагаю ниже.

Рабочий процесс

Асинхронный пакет поступает через tcp-порт в массив байтов, который затем копируется в другой массив для обработки.

Затем этот скопированный массив обрабатывается в соответствии с размером полученного пакета(каждое сообщение может быть разного размера одно за другим).

В то время как в то же время асинхронный метод получил еще одни данные, которые должны быть добавлены к массиву, используемому для обработки пакета.
Проблема приходит в методе, поскольку асинхронные данные, поступающие на TCP-порт, добавляются в любое время к массиву буферов обработки, который много раз вызывает исключения, в то время как его размер передается во время методов копирования. Я не могу заблокировать этот асинхронный вызов, как после обработки одного пакета.

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


Пользовательский интерфейс обновляется, и по действию Пользователя снова вызываются методы отправки и получения, где массив буфера приема перезаписывается новыми данными.

Совместное использование фрагмента кода:

Что я уже пробовал:

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());
        }
    }
}

1 Ответов

Рейтинг:
0

Richard MacCutchan

Вы (эффективно) используете статический буфер данных для ваших входных сообщений, поэтому каждый раз, когда какие-то данные поступают, они могут разрушить то, что уже есть. Вы должны выделить свой буфер в обработчике асинхронных событий, захватить данные и передать буфер методу, который будет их обрабатывать. Затем, если поступает больше данных, вы создадите новый буфер, а не перезаписываете то, что все еще обрабатывается.

Кроме того, в вашем коде есть следующая строка:

public const int BufferSize = 1024;

но вы все равно используете th жестко закодированное значение 1024 везде. Вы должны использовать постоянное имя, например:
// Receive buffer.
public byte[] buffer = new byte[BufferSize];

Поэтому, если вы когда-нибудь измените значение с 1024, вам не нужно будет искать код в других местах, где используется это число.