Обнаружение устройств через UPnP / SSDP |
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
МЕНЮ Главная страница Поиск Регистрация на сайте Помощь проекту Архив новостей ТЕМЫ Новости ИИ Голосовой помощник Разработка ИИГородские сумасшедшие ИИ в медицине ИИ проекты Искусственные нейросети Искусственный интеллект Слежка за людьми Угроза ИИ ИИ теория Внедрение ИИКомпьютерные науки Машинное обуч. (Ошибки) Машинное обучение Машинный перевод Нейронные сети начинающим Психология ИИ Реализация ИИ Реализация нейросетей Создание беспилотных авто Трезво про ИИ Философия ИИ Big data Работа разума и сознаниеМодель мозгаРобототехника, БПЛАТрансгуманизмОбработка текстаТеория эволюцииДополненная реальностьЖелезоКиберугрозыНаучный мирИТ индустрияРазработка ПОТеория информацииМатематикаЦифровая экономика
Генетические алгоритмы Капсульные нейросети Основы нейронных сетей Распознавание лиц Распознавание образов Распознавание речи Творчество ИИ Техническое зрение Чат-боты Авторизация |
2025-01-10 17:44 Что больше всего бесит при первом запуске устройств с управлением по Ethernet? Необходимость его искать в сети с использованием зоопарка из подходов. Тут используются программы автопоиска (например Winbox для MikroTik), дефолтные IP адреса (все эти 192.168.1.1, 192.168.100.1, 192.168.2.1 - кто во что горазд). Иногда надо со смартфона показывать QR коды в камеру устройства или передавать настройки тональными сигналами в микрофон. Мы задались целью найти стандарт для поиска устройств в сети и внедрить его в свои устройства на основе микроконтроллеров и/или одноплатных компьютеров. Это статья о стандартах, их особенностях, преодолённых трудностях и об открытом коде, который мы написали для себя и считаем лучшей в мире открытой реализацией SSDP сервера и клиента. Почему Ethernet? Кабельный Ethernet это прекрасный стандарт для индустриальных применений. Кабельное подключение надежнее беспроводного, кабели бывают с экраном, выпускаются индустриальные разъёмы RJ-45, соединение гальванически развязано (чрезмерно высокое напряжение может привести к поломке одного устройства в сети, но на другие не распространится). Соединение легко транслируется в оптоволокно медиаконвертерами, либо передаётся через коммутаторы, что обеспечивает большую дальность в интрасети, а при желании позволяет соединяться и на любой дальности по Интернету. Немаловажно, что сеть Ethernet скорее всего уже существует на объекте внедрения, так что затраты на создание канала управления минимальны. Про скорости передачи данных даже говорить не стоит. RS-232 и рядом не стоял. Ethernet почти всегда есть в одноплатных компьютерах, да и в микроконтроллеры его частенько встраивают, а иногда даже со встроенным PHY (https://www.ti.com/product/TM4C1294NCPDT). Минусы у Ethernet тоже есть. Если в протоколах RS-232, RS-485, USB пакет данных всегда будет доставлен, то в Ethernet доставка пакетов негарантированная. Либо, в случае использования поверх Ethernet протоколов гарантированной доставки (TCP), будет непредсказуемая задержка. С потерей пакетов можно бороться, используя оборудование с заведомо большей пропускной способностью, чем теоретическая пиковая нагрузка. А еще одним минусом Ethernet является плохая предсказуемость адресов устройств (что MAC, что IP) и сложная диагностика отказов связи (тут RS-232 сильно выигрывает).
В итоге самым распространённым способом уже 20 лет было назначать дефолтный IP адрес, писать его в инструкции, заставлять пользователя менять настройки своей сети (IP, маска подсети), через админку менять настройки сети устройства, восстанавливать настройки пользователя, profit. Однако, поскольку задача поиска устройств в сети довольно распространенная, то она была решена многими производителями самостоятельно. Например, в ПО Winbox для MikroTik есть механизм поиска устройств даже в другой подсети; в протоколе GigЕ Vision также есть механизм поиска устройств в сети, но только в той же; и т.д. Мы тоже в своё время сделали свой примитивный протокол обнаружения устройств через broadcast запрос на определённый порт и ожидание unicast ответа. Напрашивается мысль, что должен существовать относительно универсальный протокол поиска и настройки сетевых устройств. И лучше использовать его, чем строить очередной велосипед. Выбор протокола обнаружения устройств Сразу скажу, что мы хотим охватывать мир Windows, Linux и Mac. Поэтому нам не подходят Mac only решения, где в рамках замкнутой экосистемы всё работает само. Не подходят решения, основанные на закрытом коде, чтобы не получить vendor lock. Нужно легковесное решение, которое реально реализовать в микроконтроллере. Сам поиск нужно уметь встраивать в наш софт, но будет хорошо, если операционная система тоже умеет устройство обнаруживать. Удалось найти несколько относительно стандартных и распространенных протоколов:
Было решено попробовать найти и запустить примеры для каждой из перечисленных выше технологий обнаружения. С SSDP/UPnP всё пошло неплохо. Достаточно быстро нашёлся пример сервера, который запускаются и обнаруживаются стандартными средствами Windows (https://github.com/ZeWaren/python-upnp-ssdp-example), пример с клиентом и сервером, которые можно запустить, как на Windows, так и на Linux и они находят друг друга (https://github.com/MoshiBin/ssdpy), готовые библиотеки с примерами для C (http://miniupnp.free.fr) и Python (https://pypi.org/project/ssdpy/), а самое главное стандарт, написанный понятным языком с пояснениями и примерами (http://upnp.org/specs/arch/UPnP-arch-DeviceArchitecture-v1.1.pdf). Для поиска на базе mDNS тоже нашлись какие-то примеры. Но работали они менее стабильно. В нашей сети есть несколько сетевых принтеров. И с помощью найденных примеров их обнаружение носило вероятностный характер. С чем было связано обнаружение или необнаружение устройств, быстро понять не удалось. Также выяснилось, что встроенная поддержка mDNS появилась только в Windows 10. Для Windows 7 придётся вручную ставить Apple Bonjour или какие-то другие дополнительные компоненты (https://www.chiefdelphi.com/t/enable-mdns-on-windows/155295). Но даже в Windows 10 в зависимости от наличия обновлений поддержка может быть не полной и нацелена в основном на поиск сетевых принтеров (https://en.wikipedia.org/wiki/Multicast_DNS, https://learn.microsoft.com/en-us/answers/questions/266761/does-windows-10-support-mdns, https://superuser.com/questions/1330027/how-to-enable-mdns-on-windows-10-build-17134). Вдобавок ко всему описание mDNS в виде «машинописных» RFC (https://datatracker.ietf.org/doc/html/rfc6763) субъективно менее приятно в работе по сравнению с описанием UPnP. Первые две технологии достаточно популярны. В интернете для них можно легко найти примеры, библиотеки и обсуждения проблем. SLP на их фоне выглядит гораздо скромнее. В частности, по запросу «Service Location Protocol library» первой ссылкой у нас вышел пакет pyslp для python с полностью отсутствующим описанием: https://pypi.org/project/pyslp/ Документация потом нашлась https://pythonhosted.org/pyslp/ Но при попытке запуска примера он упал с ошибкой `local variable 'error_code' referenced before assignment`. После некоторых доработок примеры запустились, но сервера, расположенный дальше localhost всё-равно найти не получилось. Стоит отметить что для SLP есть и другие библиотеки с примерами и относительно неплохое описание стандарта (https://docs.oracle.com/cd/E19455-01/806-1412/806-1412.pdf). Но время на исследование у нас было ограничено. Поэтому мы решили остановить свой выбор на SSDP / UPnP. SSDP SSDP это Simple Service Discovery Protocol, но есть и другая расшифровка: Stupidly Simple DDoS Protocol (https://habr.com/ru/articles/332812/) из-за его подверженности amplification DDoS attack. Обнаружение устройств делается в 2 этапа (M-SEARCH и HTTP): Сначала клиент SSDP посылает специальный поисковый UDP-запрос M-SEARCH выделенному мультикастному адресу на конкретный SSDP-порт 239.255.255.250:1900 с заданным форматом пакета, в который входят следующие строки:
Этот M-SEARCH запрос пересылается всем участникам мультикастной группы, которые слушают адрес 239.255.255.250 на 1900 порту, после чего все устройства, которые подпадают под условия поиска (входят в группу устройств, запрошенных в поле ST), должны сформировать ответ и отправить его юникастной посылкой UDP-пакета на адрес и порт устройства клиента, который делал изначальный M-SEARCH запрос. Формат пакета с ответом с учетом только обязательных полей будет состоять из следующих строк:
Следующим этапом для получения более полной информации об устройстве может идти обычный HTTP-запрос XML-файла из поля LOCATION, в котором должна содержаться более развернутая информация об устройстве. Основные поля в данном XML-описании следующие:
Альтернативным способом для первого этапа является рассылка уведомлений устройством о своём присутствии в сети (NOTIFY). Однако вместо мультикаст запроса M-SEARCH с клиента и юникаст ответа от сервера в данном случае предполагается периодическая отправка NOTIFY-пакетов самим SSDP-устройством на мультикастный SSDP-адрес 239.255.255.250:1900 и постоянное прослушивание клиентом рассылки в мультикастной группе. Формат NOTIFY-пакета состоит из следующих строк:
Может взять готовые инструменты? Выбрав SSDP протокол, мы продолжили тестирование готовых инструментов для SSDP, которые делятся на программы поиска по SSDP (клиент), а также программ эмуляции устройств, отвечающих по SSDP (сервер). Стало сюрпризом, что чаще всего они несовместимы между собой. Многие авторы воспринимают SSDP, как HTTP notify поверх UDP Multicast на 239.255.255.250:1900, а дальше – кто во что горазд. Ниже идёт сводная таблица наших экспериментов с клиентами, где мы отбирали только те, что написаны на С/С++ и Python, имеют открытый код, а еще добавили встроенный инструмент Windows. Внизу добавили сравнение с тем инструментом, который написали мы:
Во-первых, почти все клиенты делятся на 2 группы. Те, кто останавливается на стадии M-SEARCH и не запрашивает XML конфигурацию на стадии HTTP. И те, кто наоборот игнорирует устройство, если на M-SEARCH оно ответило, а по HTTP - нет. Исключением оказался пакет upnpy поверх которого мы быстро написали клиентскую утилиту. Но функций для доступа ко всем полям XML описания там нет. Чтобы это исправить придётся самостоятельно парсить XML. Отдельной проблемой оказался выбор сетевого интерфейса, на котором работает сервер. Счастливы неведующие, у которых такой интерфейс один. Но в случае нескольких интерфейсов (Ethernet, WiFi, ...) логично сканировать их все. Кстати Windows так и делает. Вместо этого часто предлагалось выбирать конкретный интерфейс или довериться автовыбору. В пакете upnpy выбрать интерфейс вообще нельзя (мы не нашли как). Кроссплатформенность встречалась тоже редко. Тут лидером является библиотека miniupnp и их клиент miniupnpc. Они единственные охватили большую тройку Win/Lin/Mac. Но в своей серверной части Windows они уже не поддерживают. Кроссплатформенность и выбор сетевых интерфейсов это не так просто. Если сервер подписывается на мультикастную группу со всех адаптеров компьютера, подобный код не получается автоматически кроссплатформенным, так как процедура подписи в Linux должна проводиться с помощью использования группы 0.0.0.0, а в Windows с помощью явного перебора всех сетевых адаптеров и подписи с их реальным IP-адресом. В своём коде сервера мы сделали ответ по всем интерфейсам, а вот в доступных в интернете примерах это не всегда поддерживалось. А вообще, чего хочется от клиента поиска устройств по сети? Чтобы нашел по-максимуму. Чтобы искал быстро. Чтобы был доступ ко всей информации, а не только IP адрес и имя. Но и тонуть в информации о разных uuid:device-UUID не хочется. Кроссплатформенности хочется, чтобы можно было эту утилиту предлагать широкому кругу пользователей для поиска сделанных тобой устройств. Ну и тогда уж графический режим нужен. И тут лучше посмотреть своими глазами, кто как реализовал UX. Windows тут стоит поставить на первое место. Microsoft прорабатывает свои решения. Но средств диагностики обычно не закладывает. В итоге сетевое окружение Windows, выступая, как клиент, показывает устройство в сетевом окружении, только если пройдены оба этапа обнаружения и XML файл содержит поле gssdp-discover это утилита линуксоида. Минимум информации, консоль. HTTP предлагается запрашивать самостоятельно. miniupnpc недалеко ушло от gssdp-discover. ssdp гораздо лучше для диагностики, так как выводит всю информацию. Тоже без HTTP уровня. Но нужно учесть, что без установленного в системе Python 3.8 или выше, последняя версия ssdp работать не будет. А более ранние версии сильно отличаются по функциям. Не надо думать, что это не проблема. Сейчас 2025 год, а клиенты продолжают спрашивать работает ли наш софт на WinXP и на Python 3.4. Так что наша реализация клиента, например, сделана для Python 3.6 и выше. В UPnP Scanner совсем другой подход. Это Windows решение. Графический интерфейс. Выводится вся информация. Очень похоже на встроенное сетевое окружение Windows с GUI начального уровня, но зато доступны исходные коды. К сожалению есть еще отдельная проблема с версиями UPnP. В версиях UPnP 2.0 в XML описании не должно присутствовать поле URLBase, но для предыдущих версий UPnP оно могло присутствовать и использовалось, как абсолютный URL, к которому добавляется относительный путь presentationURL. В итоге UPnP Scanner считает невалидными и не показывает все устройства, которые соответствуют UPnP 2.0 и выше. ssdpy-discover. Мы тестировали на Windows и запускали клиента через client.py. Вывод похож на другие подобные консольные утилиты. Но основные поля есть, а второстепенные отфильтрованы. upnpy. Про эту библиотеку я уже писал выше. Она найдёт устройства на обоих уровнях поиска, но не позволит выбрать интерфейс поиска. Также клиент придётся писать самостоятельно, а доступ ко всем информационным полям устройства можно сделать через парсинг XML. Мы этого делать не стали. На скриншотах видны поля, где доступ был встроенный в библиотеку. ssdp-scan отличается тем, что умеет собирать NOTIFY сообщения. Правда занимать это может много времени. M-SEARCH он тоже умеет и быстро строит список в консоли Linux. Информация в нём скудная. Серверов мы нашли меньше, чем клиентов. Это неудивительно, ведь они больше нужны производителям оборудования, как мы. Ниже сводная таблица наших экспериментов с серверами и наша реализация для сравнения:
Все сервера умеют отвечать на M-SEARCH. Все сервера, кроме ssdpy-server, умеют отвечать на HTTP стадии и обнаружились у нас средствами Windows. А ssdpy-server у нас обнаруживался только тем клиентом, что входит с ним в одну библиотеку. Несмотря на обнаружение в Windows, чаще всего реализация стандарта полей XML описания в серверах была неполной. С кросплатформенностью еще хуже. Нужной нам тройки Win/Lin/Mac никто не собрал. Найти всегда Итак, есть много частично работающих готовых реализаций сервера и клиента, написанных под разные ОС, написанные на разных языках (нас волновал и Си, и Python). Если их скомбинировать, поотлаживать сниффером Ethernet, например Wireshark, а также почитать различные RFC, то можно довести клиент и сервер до совершенства, а дальше встраивать сервер в одноплатники и микроконтроллеры, а клиент - выдавать пользователям. Однако одна из наших целей, чтобы устройство обнаруживалось всегда. Даже, когда в нём неизвестные статические настройки сети. А описанный выше способ поиска принципиально не может работать для случая, когда маски подсети на клиенте и на сервере разные. Сервер сможет получить от клиента мультикаст M-SEARCH запрос благодаря магии мультикаста, но ответ будет через UDP unicast, который не способен дойти до клиента в другой подсети. Однако эта проблема оказалась решаема в рамках стандарта SSDP. Мы используем вторую часть SSDP — advertisement с помощью NOTIFY сообщений. Стандарт не запрещает добавлять собственные поля, где мы передаём информацию о текущих сетевых настройках. Сообщения NOTIFY идут на мультикастный SSDP-адрес, поэтому они доходят от сервера до клиента вне зависимости от того, в какой подсети они находятся. Однако вместо периодической отсылки NOTIFY мы делаем их отсылку вслед за получением M-SEARCH. То есть запрос по multicast и ответ по multicast. Таким образом, мы гарантируем, что полный список найденных устройств будет готов через 2 секунды после запроса на клиенте, а не отловом случайных нотифаев раз в 1800+ секунд. Мы не знаем кого-то еще, кто додумался до такого приёма с SSDP. DHCP и AutoIP Отдельно нужно рассмотреть случай, когда в устройстве стояло автоматическое определение настроек сети (DHCP). Если DHCP сервер недоступен или отсутствует, то устройство окажется вообще без подсети и без сетевых настроек. И тут нам на помощь приходит AutoIP. AutoIP базируется на стандарте RFC 3927 и присваивает устройству случайный, равновероятный IP-адрес из выделенной подсети 169.254.1.0-169.254.254.255. Мы генерируем его на основе MAC-адреса устройства, что тоже допустимо. AutoIP включается после нескольких неудачных попыток получить сетевые настройки от DHCP сервера. Диапазон адресов AutoIP считается всегда входящим в подсеть IPv4. Таким образом, пакеты на эти адреса направляются адресату в локальной сети напрямую, а не на шлюз, который вообще может отсутствовать. По-простому это означает, что два компьютера с поддержкой AutoIP (Windows, например), могут включить автоматическое определение настроек, не получить их от DHCP сервера, но, тем не менее, присвоить себе 2 разных IP адреса и быть связанными на L3 уровне. Всё это важно понимать для реализации в микроконтроллере, так как в Windows/Linux эти механизмы включены по-умолчанию. В микроконтроллерной библиотеке lwIP уже поддерживался механизм AutoIP, поэтому главное было включить его и понимать почему и как он работает. Beyond SSDP Теперь мы можем находить свои устройства вне зависимости от их сетевых настроек, но только на стадии M-SEARCH/NOTIFY. Стадию HTTP провести невозможно, если устройства находятся в разных подсетях. Поэтому сменить IP адрес стандартным способом через админку не получится. Как же установить новые настройки найденному устройству? Для этого мы снова использовали свойство M-SEARCH и NOTIFY пакетов, что в них можно дописывать новые поля. Неизвестные поля по стандарту отбрасываются. Мы же добавляем поддержку этих полей и прописываем туда IP/маску подсети, шлюз и флаг включения DHCP. Если наше устройство получает через мультикаст сетевые настройки, то оно их применяет. Теперь мы можем не только всегда найти своё устройство, но и сразу вбить ему новые настройки, несмотря на то, что устройство может быть в другой подсети и недоступно по UDP/TCP. Да, это функция будет работать только для нашей реализации сервера в микроконтроллере и для одноплатников (pyssdp_server) и только через клиент (revealer), который мы написали. Но по всем остальным функциям SSDP совместимость сохраняется. Тут еще возникает вопрос безопасности. Коллега может по незнанию, неосторожности или ради шутки поменять сетевые настройки и устройство перестанет быть доступно из управляющей программы, что выведет из строя всю систему, куда устройство интегрировано. Придётся звать настройщика, что обычно означает задержку в работе на часы или на дни. Поэтому добавляем защиту паролем с возможностью его смены. Добавляем альтернативный сервисный пароль, который вычисляется секретным алгоритмом на основе серийника, неизвестен пользователю, и является fallback вариантом, когда пользователь пароль поменял, но забыл. Тогда пароль под конкретный серийник можно запросить в техподдержке. Всё это работает без шифрования и не претендует на секьюрность, так как располагается, как правило, в закрытой сети. Сделали ли мы неубиваемую схему? Почти. Какой IP нельзя ставить на интерфейсе, чтобы не сломать связь с ним? Это вообще хороший вопрос для собеседования на сисадмина. Кто-то вспомнит про популярный IP localhost (127.0.0.1). Но вряд ли многие знают про диапазоны 0.0.0.0/8 и 127.0.0.0/8. Также к запретным IP был добавлен весь мультикаст 224.0.0.0/4. Вот теперь не сломаешь. Реализация в микроконтроллере Мы используем микроконтроллер Texas Instruments TM4C1294KPDT. Довольно уникальным свойством этого семейства является встроенный Ethernet PHY, что позволяет подключать микроконтроллер ножками напрямую на разъём Ethernet (со встроенными трансформаторами). Для этого микроконтроллера производитель даёт примеры, реализующие UDP сокет, а также HTTP сервер. Работает это всё поверх стека lwIP (lightweight Internet Protocol) версии 1.4.1, адаптированного для TI контроллеров с использованием FreeRTOS или без него. Этих примеров достаточно, чтобы написать свою небольшую реализацию SSDP сервера с ответом на M-SEARCH запросы, на запрос XML-файла и посылку NOTIFY пакетов. Писать реализацию с нуля было бы очень накладно. Забавно, что Texas Instruments предлагает в SDK свою утилиту поиска микроконтроллеров в сети, не являющейся реализацией одного из стандартных протокола поиска. То есть это очередной велосипед, основанный на broadcast, в ответ на который присылается IP адрес, MAC адрес, идентификатор платы, тип платы, название приложения, версия прошивки. С микроконтроллерами всё не так просто. Поэтому пришлось решить несколько проблем интеграции lwIP в нашу прошивку. Например, сначала мы вызывали методы lwIP из тех прерываний, где нам было это удобно, но иногда получали сбои. А затем мы прочитали, что библиотека lwIP не является потокобезопасной. То есть все вызовы её методов должны идти последовательно, быть сериализованы. А еще у нас не получалось сменить настройки сети на автоматические (DHCP), если текущий шлюз был не из той подсети, откуда приходит запрос на смену настроек. Комбинация странная. А причина оказалась в нехватке памяти микроконтроллера на то, чтобы одновременно ответить через несуществующий шлюз и создать нужное количество запросов к DHCP. AutoIP в такой ситуации тоже не срабатывал. Увеличили память - проблема ушла. Сервер на ПК pyssdp_server Микроконтроллерную реализацию сервера показывать довольно бессмысленно. Она завязана на конкретный чип и FreeRTOS. А вот реализацию сервера для компьютера мы вывесили в открытый доступ: https://github.com/EPC-MSU/pyssdp_server Это кроссплатформенное Win/Lin/Mac приложение для Python 3.6+, распространяемое в исходных кодах. За основу мы взяли https://github.com/ZeWaren/python-upnp-ssdp-example, который был существенно переработан, чтобы он обнаруживался в сети более стабильно и для всех сетевых интерфейсов, поддерживал обнаружение из другой подсети, поддерживал смену настроек сети через наше расширение SSDP, поддерживал HTTP сервер с переадресацией, выделение настроек в конфиг, скрипты инсталляции для systemd и т.п. Что в итоге получилось? Получилось серверное приложение, которое через релиз в виде zip архива или через git копируется на любой одноплатник (а можно компьютер с Windows или macOS), в нём меняется файл конфига на вашу информацию (название устройства, ссылки на компанию и на продукт, имя производителя и т.д.), далее сервер запускается и ваше устройство появляется в сетевом окружении Windows, ищется через любую программу поиска из таблицы выше, ищется через наш клиент (https://github.com/EPC-MSU/revealer) в других подсетях. При клике на найденное устройство обычно открывается его HTML страница, которая обслуживается встроенным HTTP сервером, использующимся и для отсылки XML описания устройства. Файлы HTML можно поменять на свои и получить полноценный статический встроенный сайт (серверные скрипты не поддерживаются). Для того чтобы серверное приложение работало и после перезагрузки, есть готовый скрипт инсталляция сервера в виде systemd сервиса. Кто регулярно превращает свои программы в сервисы, стартующие при загрузке компьютера, знает, как много может пойти не так. Наш pyssdp_server оказался не исключением. Проблему того, что сетевой интерфейс может быть не готов к открытию в нём соединений, мы предусмотрели. Сервер ожидал хотя бы одного активного сетевого интерфейса. Но когда интерфейсов 2, то сервер норовил стартовать, как только поднимется хотя бы один из них. В итоге все остальные сетевые интерфейсы не обнаруживались, пока не перезапустишь pyssdp_server. Пришлось делать фоновое отслеживание сетевых интерфейсов. Теперь, если к проводному Ethernet вдруг добавится WiFi или VPN, то обнаружение будет работать и на нём. Неожиданный нюанс, с которым мы столкнулись, что иногда на наших одноплатниках уже работает встроенная админка на 80 порту. А HTTP сервер, встроенный в SDDP, выделен на порт 5050. И хотелось бы, чтобы при клике на устройство, например в сетевом окружении Windows, открывалась именно встроенная админка. То есть хочется, чтобы presentationURL указывал на один порт, а Location - на другой. Однако в стандарте UPnP считается, что presentationURL должен быть на том же адресе и порту, что и Location поле. Пришлось это обходить через включаемый в конфиге Redirect со встроенного HTTP сервера на нужный порт главной админки. Во время тестов мы запускали сервер и клиент на одном компьютере и получали ошибку, что порт 1900 уже занят. Логично, ведь и сервер, и клиент слушают порт 1900, чтобы получать M-SEARCH и NOTIFY пакеты. И тут мы видим, что Windows прекрасно находит наш сервер на этом же компьютере, слушая тот же порт 1900. Как? А так - оказывается можно слушать один порт несколькими приложениями. Для этого есть флаги сокета В итоге мы сделали, чтобы несколько экземпляров клиентского приложения могли работать одновременно на одном компьютере, да еще и с запущенным там же сервером SSDP. Но вот 2 сервера одновременно запретили как раз через флаги Естественно мы добавили не только поиск из другой подсети, но и возможность смены настроек, чтобы работало не хуже микроконтроллеров. И тут две проблемы. Во-первых, нужно знать на каком сетевом интерфейсе меняем настройки, если их несколько. У микроконтроллера интерфейс-то один. С этим оказалось всё легко. Сервер слушает и отвечает сразу по всем интерфейсам. При этом он использует разные UUID. Поэтому запрос на смену настроек мы привязали к интерфейсу. А во-вторых, чудесный мир кроссплатформенности... Мы же хотим менять настройки на Win/Lin/Mac, а одних видов Linux существует порядка 1000 (https://habr.com/ru/companies/lanit/articles/562484/). И для каждого есть еще разные по времени версии релизов. Как тут написать единый код смены сетевых настроек? Поэтому мы пошли следующим путём: полученные настройки передаются в консольный скрипт параметрами. Скрипт, работающий на более-менее всех современных Линуксах мы написали. А если нужен скрипт для Windows, хитрого Linux или Mac, то его может написать пользователь сам. Да и вообще он сможет в скрипте кастомизировать реакцию на такое важное событие, как смена сетевых настроек. В Windows вскрылся еще ряд странностей. Во-первых, нужно отключить брандмауэр или не будет работать отсылка и получение мультикаст пакетов из другой подсети. А ведь это важное и ценное свойство SSDP, чтобы можно было всегда найти устройство. Но внутренняя жизнь сети в Windows оказалась еще сложнее. Если запустить pyssdp_server на компьютере с WiFi и проводным адаптером Ethernet, то обнаружение по проводу работает всегда. А по WiFi оно работает только первые 10 секунд после вставления или вынимания кабеля Ethernet. То есть, изменение линка на одном из адаптеров прочищает WiFi адаптер на 10 секунд и он ловит поисковые запросы. А потом перестаёт. И Wireshark не показывает больше приходящие multicast запросы. Поиском нашлась похожая проблема (https://community.intel.com/t5/Wireless/Intel-WiFi-chips-are-blocking-multicast-after-resuming-from/td-p/684849):
То есть точка доступа WiFi может считать, что мы не хотим получать мультикасты по WiFi, так как на низкоуровневом протоколе IGMP, используемом для управления мультикастными подписками, мы не сообщили ей такой трафик нам пересылать. И причина может быть в некорректном использовании IGMP в Windows или в сетевом адаптере. Дальше я решил не копать. Наш клиент Revealer В клиентском приложении хотелось достичь кроссплатформенности Win/Lin/Mac, которой нет ни у кого из готовых приложений. Хотелось соответствия стандарту SSDP, которая в примерах из интернета встречается редко. Хотелось выводить все информационные поля из стандарта на обеих стадиях обнаружения устройств (M-SEARCH и HTTP), что тоже делают нечасто. Для отладки нужно видеть все данные, а не их обрывочные представления. Например, в нашем приложении мы увидели, что одно из устройств Zyxel на M-SEARCH стадии передаёт строку SERVER с пробелом в одном из полей, что приводит к её некорректному распознаванию и в Windows устройство не показывается. Мы, кстати, про проблему с пробелами указываем в комментариях конфига, идущего в комплекте с pyssdp_server. Хотелось графического режима для того, чтобы приложение было удобно обычным пользователям. В итоге получилось вот так: Приложение с одной кнопкой и найденными устройствами, на которые можно кликать. Если нажать на значок информации, то откроется окно с полями структур описания устройства: Вверху поля HTTP стадии. Внизу поля M-SEARCH стадии. Если нажать на значок шестерёнки, который появляется только для устройств с поддержкой наших расширений протокола SSDP, то появляется окно с установкой сетевых настроек: Итог Я считаю, что у нас получилась лучшая в мире открытая кроссплатформенная реализация SSDP клиента и сервера. У неё низкий порог входа. Поэтому я мимоходом поставил pyssdp_server и на свой компьютер для домашнего кинотеатра. Самое долгое было заполнять конфиг желаемыми строчками текста. Теперь я не буду вспоминать его IP, когда захочу зайти на него по ssh. Авторы статьи: Запуниди Сергей, Надежда Тарабрина. Источник: habr.com Комментарии: |
|||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||