Форум программистов, компьютерный форум, киберфорум
Наши страницы

C# .NET

Войти
Регистрация
Восстановить пароль
 
 
Рейтинг: Рейтинг темы: голосов - 12, средняя оценка - 4.92
CSharpCraft
58 / 58 / 6
Регистрация: 09.01.2013
Сообщений: 247
#1

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

26.02.2014, 21:06. Просмотров 1862. Ответов 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 (Клиент-сервер) - потеря данных - C#
Добрый день, меня интересует такой вопрос, я имею два приложения клиент-сервер написанные на TCP с использованием асинхронной передачи...

Ошибка при передаче изображения по TCP - C#
Ошибка при передаче изображения по TCP? Клиент: Bitmap printscreen = new Bitmap(Screen.PrimaryScreen.Bounds.Width,...

Варьирование таймаута при передаче по TCP/IP - C#
Доброго дня! В общем есть задание, реализовать клиент серверное приложение, которое будет: 1. Передавать 32 кб информации,...

Проблема с объединением сообщений при передаче по TCP - C#
Вообщем делаю регистрацию и авторизацию клиента на сервере. Клиент: private void button1_Click(object sender, EventArgs e) { ...

Ошибка при передаче данных в GridView - C#
Среда: WPF Ошибка: Произошла ошибка базового поставщика в Open. Код: private void Window_Loaded(object sender,...

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

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

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

Не по теме:

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

0
turbanoff
Модератор
Эксперт Java
3981 / 3716 / 462
Регистрация: 18.05.2010
Сообщений: 9,291
Записей в блоге: 11
Завершенные тесты: 1
27.02.2014, 09:37 #5
CSharpCraft, Я не увидел в вашем коде использование SocketAsyncEventArgs.BytesTransferred. А значит вы читаете непонятно что из буфера, но точно не те байты, которые принял сокет.
0
CSharpCraft
58 / 58 / 6
Регистрация: 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
3981 / 3716 / 462
Регистрация: 18.05.2010
Сообщений: 9,291
Записей в блоге: 11
Завершенные тесты: 1
27.02.2014, 13:43 #7
Буфер не всегда заполняется полностью. Он заполняется на SocketAsyncEventArgs.BytesTransferred байт. А вы, насколько я могу судить из кода, считаете что буфер всегда полон полезных байт.
0
CSharpCraft
58 / 58 / 6
Регистрация: 09.01.2013
Сообщений: 247
27.02.2014, 14:24  [ТС] #8
Цитата Сообщение от turbanoff Посмотреть сообщение
Буфер не всегда заполняется полностью. Он заполняется на SocketAsyncEventArgs.BytesTransferred байт. А вы, насколько я могу судить из кода, считаете что буфер всегда полон полезных байт.
Сериализатор сначала читает тип пакета, и сам знает, сколько байт ему нужно прочитать из буфера

Добавлено через 3 минуты
Хотя получается, что за некоторое время буфер сокета мог заполнится несколькими пакетами, которые могут склеится
0
turbanoff
Модератор
Эксперт Java
3981 / 3716 / 462
Регистрация: 18.05.2010
Сообщений: 9,291
Записей в блоге: 11
Завершенные тесты: 1
27.02.2014, 14:53 #9
Цитата Сообщение от CSharpCraft Посмотреть сообщение
Сериализатор сначала читает тип пакета, и сам знает, сколько байт ему нужно прочитать из буфера
Но он же не знает сколько байт в буфере фактически пришло от сокета!
Представьте ситауцию: сериализатор прочитал тип пакета и понял, что пакет занимает 100 байт. Но на сокет прилетело 50 байт - это значит, что оставшаяся часть буфера будет забита мусором. И как сериализатор сможет понять, что нужно сейчас прочитать именно 50 байта, а не 100?
0
CSharpCraft
58 / 58 / 6
Регистрация: 09.01.2013
Сообщений: 247
27.02.2014, 15:05  [ТС] #10
Цитата Сообщение от turbanoff Посмотреть сообщение
Но он же не знает сколько байт в буфере фактически пришло от сокета!
Представьте ситауцию: сериализатор прочитал тип пакета и понял, что пакет занимает 100 байт. Но на сокет прилетело 50 байт - это значит, что оставшаяся часть буфера будет забита мусором. И как сериализатор сможет понять, что нужно сейчас прочитать именно 50 байта, а не 100?
Т.е. может придти только часть пакета?
0
Psilon
Master of Orion
Эксперт .NET
5916 / 4815 / 634
Регистрация: 10.07.2011
Сообщений: 14,434
Записей в блоге: 5
Завершенные тесты: 4
27.02.2014, 15:09 #11
turbanoff, как так, размер пакета разве не гарантируется tcp и даже Udp???
0
Yukikaze
340 / 319 / 19
Регистрация: 12.12.2011
Сообщений: 563
27.02.2014, 15:21 #12
Psilon, нет в TCP пакетов, там потоковая передача. Вернее есть, но они нам не подвластны.
0
turbanoff
Модератор
Эксперт Java
3981 / 3716 / 462
Регистрация: 18.05.2010
Сообщений: 9,291
Записей в блоге: 11
Завершенные тесты: 1
27.02.2014, 15:26 #13
TCP не оперирует таким понятием как пакет. Есть поток байтов, который разбивается на chunk-и. И эту разбивку вы не можете контролировать. Можете только копить chunk-и до нужного вам размера.
0
CSharpCraft
58 / 58 / 6
Регистрация: 09.01.2013
Сообщений: 247
27.02.2014, 16:08  [ТС] #14
Теперь после считывания проверяю, есть ли еще пакеты в буфере. Потом просто очищаю буфер, чтобы не создавать проблем. Так вроде работает
0
Psilon
Master of Orion
Эксперт .NET
5916 / 4815 / 634
Регистрация: 10.07.2011
Сообщений: 14,434
Записей в блоге: 5
Завершенные тесты: 4
27.02.2014, 17:18 #15
turbanoff, если посмотреть немного ниже уровнем, то TCP по-любому работает над IP, поэтому пакеты там есть. То, что они не видны "сверху", это понятно, но гарантируется доставка пакета целиком!
0
27.02.2014, 17:18
MoreAnswers
Эксперт
37091 / 29110 / 5898
Регистрация: 17.06.2006
Сообщений: 43,301
27.02.2014, 17:18
Привет! Вот еще темы с ответами:

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

Потеря dataset при копировании - C#
Добрый день! Видимо совсем идиотический вопрос, но: есть следующий код DataSet qqq = new DataSet(); DataSet www = new...

Не мог понять в чем ошибка в передаче данных по локальной сети - C#
Реализовал программу для передачи сообщений по локальной сети(Интерфейс на скр.1(прикрепил к сообщению)) и создал сервер (C++) которая...

.NET 3.x итерация при передаче аргументов в функцию - C#
Доброго времени суток хочу передать в функцию аргумент итерируемый при передаче то есть чтобы это выглядело так : ...


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

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

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