Потеря данных TCP IP C # - PullRequest
       16

Потеря данных TCP IP C #

2 голосов
/ 03 апреля 2011

Вот мой код:

private void OnReceive(IAsyncResult result)
{
NetStateObject state = (NetStateObject)result.AsyncState;

Socket client = state.Socket;

int size = client.EndReceive(result);

byte[] data = state.Buffer;

object data = null;

using (MemoryStream stream = new MemoryStream(data))
{
    BinaryFormatter formatter = new BinaryFormatter();

    data = formatter.Deserialize(stream);
}

//todo: something with data

client.BeginReceive(
    state.Buffer,
    0,
    NetStateObject.BUFFER_SIZE,
    SocketFlags.None,
    OnReceive,
    state
    );
}

state.Buffer имеет максимальный размер NetStateObject.BUFFER_SIZE (1024). Во-первых, это слишком большой или слишком маленький? Во-вторых, если я отправлю что-то большее, моя десериализация испортится, потому что объект, который он пытается десериализовать, не имеет всей информации (потому что не все данные были отправлены). Как мне убедиться, что все мои данные получены, прежде чем я попытаюсь их сконструировать и что-то с ними сделать?

Завершенный рабочий код

        private void OnReceive(IAsyncResult result)
    {
        NetStateObject state = (NetStateObject)result.AsyncState;

        Socket client = state.Socket;

        try
        {
            //get the read data and see how many bytes we received
            int bytesRead = client.EndReceive(result);

            //store the data from the buffer
            byte[] dataReceived = state.Buffer;

            //this will hold the byte data for the number of bytes being received
            byte[] totalBytesData = new byte[4];

            //load the number byte data from the data received
            for (int i = 0; i < 4; i++)
            {
                totalBytesData[i] = dataReceived[i];
            }

            //convert the number byte data to a numan readable integer
            int totalBytes = BitConverter.ToInt32(totalBytesData, 0);

            //create a new array with the length of the total bytes being received
            byte[] data = new byte[totalBytes];

            //load what is in the buffer into the data[]
            for (int i = 0; i < bytesRead - 4; i++)
            {
                data[i] = state.Buffer[i + 4];
            }

            //receive packets from the connection until the number of bytes read is no longer less than we need
            while (bytesRead < totalBytes + 4)
            {
                bytesRead += state.Socket.Receive(data, bytesRead - 4, totalBytes + 4 - bytesRead, SocketFlags.None);
            }

            CommandData commandData;

            using (MemoryStream stream = new MemoryStream(data))
            {
                BinaryFormatter formatter = new BinaryFormatter();

                commandData = (CommandData)formatter.Deserialize(stream);
            }

            ReceivedCommands.Enqueue(commandData);

            client.BeginReceive(
                state.Buffer,
                0,
                NetStateObject.BUFFER_SIZE,
                SocketFlags.None,
                OnReceive,
                state
                );

            dataReceived = null;
            totalBytesData = null;
            data = null;
        }
        catch(Exception e)
        {
            Console.WriteLine("***********************");
            Console.WriteLine(e.Source);
            Console.WriteLine("***********************");
            Console.WriteLine(e.Message);
            Console.WriteLine("***********************");
            Console.WriteLine(e.InnerException);
            Console.WriteLine("***********************");
            Console.WriteLine(e.StackTrace);
        }
    }

Ответы [ 2 ]

4 голосов
/ 03 апреля 2011

Как мне убедиться, что все мои данные были получены, прежде чем я попытаюсь их сконструировать и что-то с ними сделать?

Вы должны реализовать некоторый протокол, чтобы вы знали.

Хотя протокол TCP надежен, он не гарантирует, что данные от одной записи на одном конце сокета будут отображаться как одно чтение на другом конце: повторные попытки, фрагментация пакетов и MTU могут привести к получению данных в разных размерные единицы получателем. Вы получите данные в правильном порядке.

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

Итак, отправитель отправляет: - Тип сообщения - версия сообщения - Размер сообщения (в байтах)

И получатель будет зацикливаться, выполняя чтение с буфером и добавляя его в главный буфер (MemoryStream подходит для этого). Как только полный заголовок получен, он знает, когда были получены полные данные.

(Другой способ - включить какой-либо шаблон в качестве маркера «конец сообщения», но затем вам нужно обработать ту же последовательность байтов, встречающуюся в содержимом - трудно сделать, если данные являются двоичными, а не текстовыми.)

4 голосов
/ 03 апреля 2011

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

Существует два распространенных способа пакетирования:

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

Вы сохраняете размер логического пакета в начале этого пакета. Затем вы читаете, пока не получите достаточно байтов, чтобы заполнить пакет и начать десериализацию.

...