клиент - отправка данных на сервер c#




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

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

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

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

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

Поэтому отправитель отправляет: - Тип сообщения - Версия сообщения - Размер сообщения (в байтах)

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

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

Вот мой код:

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

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

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

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

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





packet