Лирическое вступление. Мое погружение в SNMP.
Компания, в которой я имел счастье работать до начала 2011 года занималась, ни много ни мало, разработкой, изготовлением, внедрением и поддержкой собственного телекоммуникационного оборудования. Занимаясь разработкой программного комплекса призванного мониторить и управлять всем этим хозяйством из тысяч разнотипных устройств вполне логично было столкнуться с использованием протокола SNMP.
Надо сказать, что лично я не считаю протокол SNMP верхом совершенства, хотя определённая красота, в нем, разумеется имеется. Долгое время для мониторинга и управления нашим хозяйством я использовал свою собственную двухуровневую систему протоколов, разрешающей нам все прихоти наших концепций управления и мониторинга, а также физических каналов передачи данных. Однако для поддержания веса продукта требуется поддержка стандартов, поэтому пришлось наконец засучить рукава и вплотную заняться SNMP.
Комплекс, который следовало адаптировать под SNMP представляет собой сложную клиент-серверную систему состоящую из серверных сетей и произвольного числа клиентов.
У каждого творца есть свой порог детализации используемых им объектов. Все чаще я встречаю разработчиков, которые стараются мыслить категориями наиболее верхних порядков. Меня, наоборот, более привлекают низкоуровневые категории. Отчасти поэтому, я решил подойти к SNMP на максимально низком уровне, т.е. с нуля, без использования какого-либо готового кода. Такой подход не только удовлетворил моё любопытство (за счёт компании, разумеется), но и дал максимальную свободу во внедрении протокола в готовую систему на обоих её концах (клиенте и сервере).
Благо, что сама сущность протокола в системе была выделена на уровне готовой спецификации. Любой новый протокол мог быть вставлен в систему обычной динамической библиотекой, удовлетворяющей сформулированной спецификации. Протокольная сущность должна была не только разрешать проблемы кодирования и декодирования согласно особенностям самого протокола, но и конвертировать логику интерфейса верхних слоев системы в систему запросов конкретного протокола. На момент начала внедрения SNMP система уже работала с двумя разными протоколами (родным K095 и неким msk-json, используемом на одном из типов серверов компании).
Первое с чем я столкнулся, это низкая детализация и популяризация вопроса несмотря на обширное количество Интернет-ресурсов, посвящённых вопросам SNMP. Опираясь на найденные мною материалы крайне тяжело было понять, что же собственно надо написать, чтобы ЭТО заработало.
Прошло около недели изучения разного рода документации, пока, наконец в моей голове появилось понимание таких важных для программирования протокола терминов, как SNMP, MIB, ASN.1, BER в едином контексте поставленной задачи.
Наконец я понял как от терминов верхнего уровня протокола SNMP (запросы get, get-next, set и пр.) перейти к последовательности байт, которые отражают эти запросы в сетевом трафике. С этого момента стало особенно интересно. Началось непосредственное кодирование.
Сначала написал код синтеза запросов и отправил его на готовый сервер. К радости, получил с сервера ответ. Написал парсер ответов. Подошёл вплотную к парсингу MIB-файлов. За два дня удалось выполнить синтаксический разбор MIB-а нашей компании. В общем, получилось некое тестовое приложение, написанное полностью с нуля (относительно категорий протокола SNMP), парсящее MIB-файл параметров устройств компании и выполняющих по нему систему запросов. В общем (творцы меня поймут) радости не было предела.
Кроссплатформенный парсинг конца строки
Теперь, все-таки, пора перейти к главному. Все написанное выше можно рассматривать как затянувшееся вступление, к тому, о чем, собственно, и хотелось рассказать мне в этом посте.
Дело в том, что в процессе парсинга MIB-файла, в очередной раз встала задача обхода символов конца строки конечным автоматом лексического анализатора. Казалось бы тривиальная задача, но, никогда ранее мне не удавалось решение, которое бы я посчитал красивым. На этот раз, кажется, получилось.
Напомню, что конец строки, в разных операционных системах представляется по разному и используется для это манипуляция с кодами 0×0A (line feed) и 0×0D (carret return). Такая ситуация усложняет реализацию счетчика строк при потоковом парсинге файла. Так как речь идет о кроссплатформенной системе, то вопрос особенно актуален. Хотя, даже если речь идёт о какой-то конкретной платформе, не факт, что вы будете иметь дело с файлом подготовленным на такой же платформе, в редакторе, который формирует окончание строки в удобном вам виде.
Детализирую проблему.
1. Мы не знаем, один или два кода используются для разделения строк.
2. Мы не знаем, является ли первым (а может и единственным) кодом, код 0×0A или код 0×0D.
Такая постановка вопроса не позволяет тупо инкрементировать счётчик строк по какому-либо из этих двух кодов. Для тех, кто ещё мучается данной проблемой, возможно, будет интересно посмотреть моё решение.
// m_ucNL - флаг переноса // m_iRow - счетчик строк // m_iCol - счетчик колонок (позиция символа в строке) // берем очередной символ из потока unsigned char ch = pchData[i++]; if (ch == 0x0a || ch == 0x0d) { if (m_ucNL == 0) m_ucNL = ch; /* такое разделение условий позволит переключатся только по первому коду!!! */ if (m_ucNL == ch) { ++m_iRow; // инкрементируем счетчик строк } m_iCol = 0; } else { m_ucNL = 0; ++m_iCol; }
Дополнение от 14 февраля 2012 года
Один из моих знакомых, читателей данного блога, недавно сообщил мне, что нашел ошибку в данном счетчике строк. По его мнению, последовательности типа "\x0A\x0A" (*nix) или "\x0D\x0A\x0D\x0A" (DOS/Windows) будут восприняты таким счетчиком как одна строка.
На случай, если кто-то еще опасается за такую ошибку счетчика, сообщаю, что все нормально. Ошибки нет. Обратите внимание, что флаг m_ucNL приобретает значение только если прежде его значение было равно нулю. Следовательно в последовательности символов разделителей флаг получит значение первого символа. Далее, обратите внимание на то, что инкремент счетчика строк возникает только при условии равенства флага m_ucNL текущему символу разделителя, т.е. счетчик строк будет инкрементироваться только по каждой новой строке.
Комментариев нет:
Отправить комментарий