Состояние гонки сетевых данных из ада
Я писал сетевой клиент / сервер (Windows XP / C #) для работы с аналогичным приложением на очень старой (Encore 32/77) рабочей станции, написанной другим разработчиком.
По сути, приложение делило / манипулировало определенными данными на хосте, чтобы контролировать процесс хоста, на котором запущена система, с помощью нашего модного интерфейса монитора с сенсорным экраном на базе нескольких ПК.
Он сделал это с 3-х уровневой структурой. Процесс связи считывал / записывал данные на / с хоста, делал все необходимые преобразования формата (порядковый номер, формат с плавающей запятой и т. Д.) И записывал / считывал значения в / из базы данных. База данных действовала как посредник данных между интерфейсами связи и сенсорного экрана. Приложение сенсорного интерфейса генерировало интерфейсы сенсорного экрана в зависимости от того, сколько мониторов было подключено к ПК (это автоматически обнаруживалось).
В заданном временном интервале пакет значений между хостом и нашим компьютером мог передавать только 128 значений максимум по каналу за раз с максимальной задержкой ~ 110 мс на передачу в оба конца (UDP использовался с прямым соединением Ethernet через x-over между Компьютеры). Таким образом, количество разрешенных переменных на основе переменного количества подключенных сенсорных экранов находилось под строгим контролем. Кроме того, хост (хотя и имеющий довольно сложную многопроцессорную архитектуру с шиной совместно используемой памяти, используемой для вычислений в реальном времени) имел вычислительную мощность моего сотового телефона примерно в 1/100, поэтому ему было поручено выполнять как можно меньше обработки, и его сервер / client должен был быть написан на ассемблере, чтобы убедиться в этом (на хосте выполнялась полная симуляция в реальном времени, на которую не могла повлиять наша программа).
Вопрос был. Некоторые значения при изменении на сенсорном экране не будут принимать только что введенное значение, но будут случайным образом переключаться между этим значением и предыдущим значением. Это и только на нескольких определенных значениях на нескольких определенных страницах с определенной комбинацией страниц когда-либо показывало признак. Мы почти полностью пропустили проблему, пока не начали ее прорабатывать в процессе первоначального принятия клиентов
Чтобы определить проблему, я выбрал одно из колеблющихся значений:
- Я проверил приложение Touchscreen, оно качалось
- Я проверил базу данных, колеблюсь
- Я проверил приложение связи, колеблюсь
Затем я запустил wireshark и начал вручную декодировать захват пакетов. Результат:
- Не колеблется, но пакеты выглядят неправильно, слишком много данных.
Я сто раз перебирал каждую деталь кода связи, не обнаружив ни ошибки, ни ошибки.
Наконец, я начал отправлять электронные письма другому разработчику, подробно спрашивая, как работает его конец, чтобы узнать, что я пропустил. Тогда я нашел это.
Очевидно, что когда он отправлял данные, он не сбрасывал массив данных перед передачей, поэтому, по сути, он просто перезаписывал последний использованный буфер новыми значениями, перезаписывая старые, но старые данные, не перезаписанные, все еще передавались.
Таким образом, если значение было в позиции 80 массива данных, а список запрашиваемых значений изменился до менее 80, но это же значение содержалось в новом списке, тогда оба значения будут существовать в буфере данных для этого конкретного буфера в любом данное время.
Значение, считываемое из базы данных, зависело от временного интервала, когда пользовательский интерфейс запрашивал значение.
Исправление было до боли простым. Считайте количество элементов, поступающих в буфер данных (он фактически содержался как часть протокола пакета), и не считывайте буфер за этим количеством элементов.
Уроки выучены:
Не принимайте современные вычислительные мощности как должное. Было время, когда компьютеры не поддерживали Ethernet, а очистка массива считалась дорогой. Если вы действительно хотите увидеть, как далеко мы продвинулись, представьте систему, которая практически не имеет формы динамического распределения памяти. То есть исполнительный процесс должен был предварительно выделить всю память для всех программ по порядку, и ни одна программа не могла вырасти за эту границу. То есть выделение большего объема памяти программе без перекомпиляции всей системы может привести к серьезному сбою. Интересно, будут ли когда-нибудь люди говорить о днях перед сборкой мусора в одном свете?
При работе в сети с пользовательскими протоколами (или обработке двоичного представления данных в целом) обязательно читайте спецификацию, пока не поймете каждую функцию каждого значения, передаваемого по каналу. Я имею ввиду, читай, пока твои глаза не болят. Люди обрабатывают данные, манипулируя отдельными битами или байтами, имеют очень умные и эффективные способы ведения дел. Отсутствие мельчайших деталей может сломать систему.
Общее время исправления составило 2-3 дня, и большую часть времени я потратил на работу над другими вещами, когда я разочаровался в этом.
SideNote: рассматриваемый хост-компьютер не поддерживает Ethernet по умолчанию. Карта для ее вождения была изготовлена на заказ и модифицирована, а стек протоколов практически не существовал. Разработчик, с которым я работал, был чертовским программистом, он не только реализовал урезанную версию UDP и минимальный поддельный стек Ethernet (процессор не был достаточно мощным для обработки полного стека Ethernet) в системе для этого проекта но он сделал это менее чем за неделю. Он также был одним из руководителей проектных команд, которые изначально разрабатывали и программировали ОС. Скажем так: все, что ему когда-либо приходилось рассказывать о компьютерах / программировании / архитектуре, независимо от того, сколько времени я уже выучил или сколько я уже новичок, я слушал каждое слово.