Форум программистов, компьютерный форум, киберфорум
Наши страницы
C# .NET
Войти
Регистрация
Восстановить пароль
 
 
Рейтинг: Рейтинг темы: голосов - 12, средняя оценка - 4.92
CSharpCraft
58 / 58 / 9
Регистрация: 09.01.2013
Сообщений: 247
#1

Потеря данных при передаче по TCP - C#

26.02.2014, 21:06. Просмотров 2092. Ответов 25
Метки нет (Все метки)

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

на двух компах проблема появляется быстрее, чем на одном

Сокет клиента (используется и на серве и на клиенте; только функционал, касающийся вопроса):
C#
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
        private ManualResetEvent receiveDone;
        private Action<ReceiveEventArgs> receiveHandle;
        private bool isReceiveCycleBegin;
 
        /// <summary>
        /// Обрабатывает пакет
        /// </summary>
        private void ReceiveCallback(object s, SocketAsyncEventArgs e)
        {
            if (isReceiveCycleBegin && receiveHandle != null)
            {
                byte[] buffer = e.Buffer;
 
                IMPSerializable output;
                ReceiveEventArgs eventArgs = new ReceiveEventArgs(this);
                Serializer.Unboxing(NetworkTransfer.TransferReflectionSource, ref buffer,
                    out eventArgs.PacketSizeInBytes, out output, out eventArgs.PacketType);
                eventArgs.Packet = (IPacketBase)output;
 
                //Вызов внешнего обработчика
                receiveHandle(eventArgs);
            }
 
            receiveDone.Set();
        }
 
        /// <summary>
        /// Начинает асинхронный прием сообщений для текущего сокета, выполняет указанный метод, когда получает пакет.
        /// Функция отключается автоматически при разрыве соединения.
        /// DESC: ОТДЕЛЬНЫЙ ПОТОК
        /// </summary>
        public void StartAsyncReceive(Action<ReceiveEventArgs> asyncResultHandler)
        {
            if (isReceiveCycleBegin)
                throw new InvalidOperationException();
            if (rasyncResultHandler == null)
                throw new ArgumentNullException("asyncResultHandler ");
 
            isReceiveCycleBegin = true;
 
            receiveDone = new ManualResetEvent(true);
            receiveHandle = asyncResultHandler;
 
            SocketAsyncEventArgs receiveEventArgs = new SocketAsyncEventArgs();
            receiveEventArgs.RemoteEndPoint = socket.RemoteEndPoint;
            receiveEventArgs.SetBuffer(new byte[GameProcessSettings.SYS_ALL_BUFFERS_SIZE], 0, GameProcessSettings.SYS_ALL_BUFFERS_SIZE);
            receiveEventArgs.Completed += ReceiveCallback;
 
            while (isReceiveCycleBegin)
            {
                receiveDone.Reset();
 
                //Ожидаем следующего сообщения
                socket.ReceiveAsync(receiveEventArgs);
 
                receiveDone.WaitOne();
            }
            receiveHandle = null;
        }
 
        /// <summary>
        /// Отправляет заполненные байты из указанного буфера
        /// </summary>
        public void SendAsync(ExternalBuffer buffer, out bool error)
        {
            //DESC: В buffer уже записаны байты пакета
 
            try
            {
                socket.BeginSend(buffer.buffer, 0, buffer.Length, SocketFlags.None, x =>
                    {
                        if (!x.IsCompleted && AsyncError != null)
                            AsyncError();
                error = false;
            }
            catch (SocketException)
            {
                error = true;
            }
            catch (Exception)
            {
#if DEBUG
                throw;
#endif
                error = true;
                //
#endif
            }
        }
receiveHandle - внешний обработчик. На сервере сразу выполняет логику взаимодействия для пакетов (ну добавляет их в очередь, а затем другой поток быстренько подбирает и выполняет их последовательно, для избежания параллелизма), на клиенте делает практически тоже самое

На сервере создается отдельный поток, из которого вызывается функция StartAsyncReceive в сокете клиента.

P.S. Любые вылеты, ошибки, и т.п. фиксируются, пакеты логируются, поэтому проблема явно в обмене.
P.S2. С Begin/End receive и синхронными вариантом receive в разных комбинациях с обычным и асинхронным send та же проблема(((
0
Надоела реклама? Зарегистрируйтесь и она исчезнет полностью.
Similar
Эксперт
41792 / 34177 / 6122
Регистрация: 12.04.2006
Сообщений: 57,940
26.02.2014, 21:06
Я подобрал для вас темы с готовыми решениями и ответами на вопрос Потеря данных при передаче по TCP (C#):

TCP (Клиент-сервер) - потеря данных
Добрый день, меня интересует такой вопрос, я имею два приложения клиент-сервер...

Варьирование таймаута при передаче по TCP/IP
Доброго дня! В общем есть задание, реализовать клиент серверное приложение,...

Ошибка при передаче изображения по TCP
Ошибка при передаче изображения по TCP? Клиент: Bitmap printscreen = new...

Проблема с объединением сообщений при передаче по TCP
Вообщем делаю регистрацию и авторизацию клиента на сервере. Клиент: private...

Защита при передаче сообщения по протоколу TCP и еще кое-что
Значит накопилось много вопросов на счет протокола TCP и передаче по нему...

Потеря данных при xml десериализации
Всем привет! Столкнулся с проблемой потери данных при xml десериализации...

25
Martovskij
99 / 39 / 12
Регистрация: 30.01.2011
Сообщений: 129
Завершенные тесты: 4
26.02.2014, 21:23 #2
Дык может теряются где выше по стеку receiveHandle(eventArgs)?
0
Psilon
Master of Orion
Эксперт .NET
5981 / 4834 / 901
Регистрация: 10.07.2011
Сообщений: 14,439
Записей в блоге: 5
Завершенные тесты: 4
26.02.2014, 21:50 #3
CSharpCraft, во-первых для игр нужно использовать UDP. А во-вторых страшно за вас, если вы даже с TCP умудряетесь терять пакеты
0
CSharpCraft
58 / 58 / 9
Регистрация: 09.01.2013
Сообщений: 247
26.02.2014, 22:01  [ТС] #4
Цитата Сообщение от Martovskij Посмотреть сообщение
Дык может теряются где выше по стеку receiveHandle(eventArgs)?
Неа. Я проверял, там все логируется

Добавлено через 36 секунд
Цитата Сообщение от Psilon Посмотреть сообщение
А во-вторых страшно за вас, если вы даже с TCP умудряетесь терять пакеты

Не по теме:

Мне уже самому страшно, раз такая ерунда происходит

0
turbanoff
Эксперт Java
4008 / 3743 / 739
Регистрация: 18.05.2010
Сообщений: 9,323
Записей в блоге: 11
Завершенные тесты: 1
27.02.2014, 09:37 #5
CSharpCraft, Я не увидел в вашем коде использование SocketAsyncEventArgs.BytesTransferred. А значит вы читаете непонятно что из буфера, но точно не те байты, которые принял сокет.
0
CSharpCraft
58 / 58 / 9
Регистрация: 09.01.2013
Сообщений: 247
27.02.2014, 13:38  [ТС] #6
Цитата Сообщение от turbanoff Посмотреть сообщение
Я не увидел в вашем коде использование SocketAsyncEventArgs.BytesTransferred. А значит вы читаете непонятно что из буфера, но точно не те байты, которые принял сокет.
В смысле?

Добавлено через 1 минуту
Из этого я могу поставить только if (SocketAsyncEventArgs.BytesTransferred != 0 && ...)

Добавлено через 8 минут
Кстати: если операция завершается синхронно, то будет ли вызвано Completed, или обработку нужно вызвать в ручную?
C#
1
2
3
4
5
6
7
                //Ожидаем следующего сообщения
                bool willRaiseEvent = socket.ReceiveAsync(receiveEventArgs);
                if (!willRaiseEvent)
                {
                    //Операция завершилась синхронно (без ожидания)
                    //Вызвать обработчик
                }
Добавлено через 2 минуты
Да, скорее всего именно так

Возвращает значение true, если операция ввода-вывода находится в состоянии ожидания. По завершении операции создается событие SocketAsyncEventArgs.Completed в параметре e.
Возвращает значение false, если операция ввода-вывода завершена синхронно. В данном случае событие SocketAsyncEventArgs.Completed на параметре e не будет создано и объект e, передаваемый как параметр, можно изучить сразу после получения результатов вызова метода для извлечения результатов операции.
Добавлено через 36 минут
Хотя проблема осталась
0
turbanoff
Эксперт Java
4008 / 3743 / 739
Регистрация: 18.05.2010
Сообщений: 9,323
Записей в блоге: 11
Завершенные тесты: 1
27.02.2014, 13:43 #7
Буфер не всегда заполняется полностью. Он заполняется на SocketAsyncEventArgs.BytesTransferred байт. А вы, насколько я могу судить из кода, считаете что буфер всегда полон полезных байт.
0
CSharpCraft
58 / 58 / 9
Регистрация: 09.01.2013
Сообщений: 247
27.02.2014, 14:24  [ТС] #8
Цитата Сообщение от turbanoff Посмотреть сообщение
Буфер не всегда заполняется полностью. Он заполняется на SocketAsyncEventArgs.BytesTransferred байт. А вы, насколько я могу судить из кода, считаете что буфер всегда полон полезных байт.
Сериализатор сначала читает тип пакета, и сам знает, сколько байт ему нужно прочитать из буфера

Добавлено через 3 минуты
Хотя получается, что за некоторое время буфер сокета мог заполнится несколькими пакетами, которые могут склеится
0
turbanoff
Эксперт Java
4008 / 3743 / 739
Регистрация: 18.05.2010
Сообщений: 9,323
Записей в блоге: 11
Завершенные тесты: 1
27.02.2014, 14:53 #9
Цитата Сообщение от CSharpCraft Посмотреть сообщение
Сериализатор сначала читает тип пакета, и сам знает, сколько байт ему нужно прочитать из буфера
Но он же не знает сколько байт в буфере фактически пришло от сокета!
Представьте ситауцию: сериализатор прочитал тип пакета и понял, что пакет занимает 100 байт. Но на сокет прилетело 50 байт - это значит, что оставшаяся часть буфера будет забита мусором. И как сериализатор сможет понять, что нужно сейчас прочитать именно 50 байта, а не 100?
0
CSharpCraft
58 / 58 / 9
Регистрация: 09.01.2013
Сообщений: 247
27.02.2014, 15:05  [ТС] #10
Цитата Сообщение от turbanoff Посмотреть сообщение
Но он же не знает сколько байт в буфере фактически пришло от сокета!
Представьте ситауцию: сериализатор прочитал тип пакета и понял, что пакет занимает 100 байт. Но на сокет прилетело 50 байт - это значит, что оставшаяся часть буфера будет забита мусором. И как сериализатор сможет понять, что нужно сейчас прочитать именно 50 байта, а не 100?
Т.е. может придти только часть пакета?
0
Psilon
Master of Orion
Эксперт .NET
5981 / 4834 / 901
Регистрация: 10.07.2011
Сообщений: 14,439
Записей в блоге: 5
Завершенные тесты: 4
27.02.2014, 15:09 #11
turbanoff, как так, размер пакета разве не гарантируется tcp и даже Udp???
0
Yukikaze
341 / 320 / 48
Регистрация: 12.12.2011
Сообщений: 563
27.02.2014, 15:21 #12
Psilon, нет в TCP пакетов, там потоковая передача. Вернее есть, но они нам не подвластны.
0
turbanoff
Эксперт Java
4008 / 3743 / 739
Регистрация: 18.05.2010
Сообщений: 9,323
Записей в блоге: 11
Завершенные тесты: 1
27.02.2014, 15:26 #13
TCP не оперирует таким понятием как пакет. Есть поток байтов, который разбивается на chunk-и. И эту разбивку вы не можете контролировать. Можете только копить chunk-и до нужного вам размера.
0
CSharpCraft
58 / 58 / 9
Регистрация: 09.01.2013
Сообщений: 247
27.02.2014, 16:08  [ТС] #14
Теперь после считывания проверяю, есть ли еще пакеты в буфере. Потом просто очищаю буфер, чтобы не создавать проблем. Так вроде работает
0
Psilon
Master of Orion
Эксперт .NET
5981 / 4834 / 901
Регистрация: 10.07.2011
Сообщений: 14,439
Записей в блоге: 5
Завершенные тесты: 4
27.02.2014, 17:18 #15
turbanoff, если посмотреть немного ниже уровнем, то TCP по-любому работает над IP, поэтому пакеты там есть. То, что они не видны "сверху", это понятно, но гарантируется доставка пакета целиком!
0
turbanoff
Эксперт Java
4008 / 3743 / 739
Регистрация: 18.05.2010
Сообщений: 9,323
Записей в блоге: 11
Завершенные тесты: 1
27.02.2014, 20:05 #16
Psilon, Пакеты есть в IP, но не TCP. И гарантий доставки IP-пакетов нет.
Цитата Сообщение от Psilon Посмотреть сообщение
То, что они не видны "сверху", это понятно, но гарантируется доставка пакета целиком!
Это не значит, что сокет доставленным пакетом заполнит весь буфер, который ему предоставил пользователь.

Добавлено через 2 минуты
Psilon, Не понимаю почему вы спорите, ведь это очень просто проверить, что SocketAsyncEventArgs.BytesTransferred не всегда равен размеру буфера.
0
Psilon
Master of Orion
Эксперт .NET
5981 / 4834 / 901
Регистрация: 10.07.2011
Сообщений: 14,439
Записей в блоге: 5
Завершенные тесты: 4
27.02.2014, 22:13 #17
turbanoff, я не спорю, я пытаюсь понять работу, благо в конце семестра как раз сети сдавать, так что хочется разобраться. Судил по тому, что UDP гарантирует, что пакет будет доставлен целиком или не будет доставлен вообще, логично предположить, что TCP тоже это гарантирует (на одном из уровней асбтракции), для вызывающего кода он, конечно, устанавливает "соединение", но по факту оперирует теми же пакетами ведь.

А тут, насколько я понял, то ли буфер не был полностью очищен, то ли еще что, но сообщение пришло целиком, но либо не вместилось в буфер, либо заняло меньше, чем его объем, поэтому были мусорные байты. Если не прав - поправьте, буду только рад
0
xoraxax
1223 / 1178 / 348
Регистрация: 05.07.2013
Сообщений: 5,716
Завершенные тесты: 1
28.02.2014, 02:42 #18
UDP вообще ничего не гарантирует (единственно контрольную сумму проверяет), TCP гарантирует доставку при том в правильном порядке. То, что отправляется, в общем, в обоих случаях вполне можно назвать пакетами, сегментами, как угодно еще, просто в TCP реализованы дополнительные механизмы, передаются дополнительные служебные данные.
0
Psilon
Master of Orion
Эксперт .NET
5981 / 4834 / 901
Регистрация: 10.07.2011
Сообщений: 14,439
Записей в блоге: 5
Завершенные тесты: 4
28.02.2014, 10:07 #19
xoraxax, да я представляю, как именно функционирует tcp
А про контрольк сумму я и имел ввиду.
0
Убежденный
Ушел с форума
Эксперт С++
15941 / 7252 / 1176
Регистрация: 02.05.2013
Сообщений: 11,637
Записей в блоге: 1
Завершенные тесты: 1
28.02.2014, 14:59 #20
Цитата Сообщение от xoraxax Посмотреть сообщение
TCP гарантирует доставку при том в правильном порядке.
TCP не гарантирует доставку.
0
28.02.2014, 14:59
MoreAnswers
Эксперт
37091 / 29110 / 5898
Регистрация: 17.06.2006
Сообщений: 43,301
28.02.2014, 14:59
Привет! Вот еще темы с решениями:

Ошибка при передаче данных в GridView
Среда: WPF Ошибка: Произошла ошибка базового поставщика в Open. Код: ...

Некорректное отображение кириллицы при передаче данных от сервера клиенту
Здравствуйте. ПИшу проект, в котором нужно передавать текст от сервера к...

Проблеммы при скрытом запуске дочерних окон сторонних приложений и передаче в них данных
Посоветуйте пожалуйста, как решить следующую проблемму: из основой программы...

Потеря dataset при копировании
Добрый день! Видимо совсем идиотический вопрос, но: есть следующий код...


Искать еще темы с ответами

Или воспользуйтесь поиском по форуму:
20
Ответ Создать тему
Опции темы

КиберФорум - форум программистов, компьютерный форум, программирование
Powered by vBulletin® Version 3.8.9
Copyright ©2000 - 2018, vBulletin Solutions, Inc.
Рейтинг@Mail.ru