Наши партнеры

UnixForum





Библиотека сайта rus-linux.net

Возможности тулкита GTK+ и сопутствующих библиотек

Низкоуровневые функции для работы с сетью

Автор: A. Панин
Дата публикации: 11 июня 2015 г.

4. Пример использования низкоуровневых функций для работы с протоколом UDP

Второе приложение предназначено для получения информации о текущем времени от сервера синхронизации времени. Но в данном случае будет использоваться не полноценный протокол NTP, а его упрощенная версия, а именно, протокол SNTP, описанный в стандарте RFC 4330. Протокол SNTP предусматривает как возможность одноадресной передачи данных, так и возможности многоадресной и широковещательной передачи данных, которые мы не не будем как-либо затрагивать. Данный протокол является бинарным прикладным протоколом и рекомендуется к использованию на встраиваемых устройствах, для корректного функционирования которых не требуется осуществления синхронизации времени с высокой точностью. В случае одноадресной передачи данных протокол предусматривает передачу дейтаграммы установленной структуры по протоколу UDP на порт сервера 123 и прием такой же дейтаграммы с информацией о текущем времени от сервера. Структура дейтаграммы (без необязательных полей для аутентификации) представлена на Рисунке 3, а структура поля команд и информации - на Рисунке 4.

Структура дейтаграммы протокола SNTP
Рисунок 3 - Структура дейтаграммы протокола SNTP

Структура поля команд и информации дейтаграммы протокола SNTP
Рисунок 4 - Структура поля команд и информации дейтаграммы протокола SNTP

Дейтаграмма состоит из четырех 4-х байтовых полей для передачи метаданных и четырех 8-ми байтовых полей для передачи меток времени, причем каждое из полей меток времени состоит из 32-битного поля для передачи количества секунд, прошедших с начала эпохи NTP (текущая эпоха начинается с 0 часов 1 января 1900 года, что раньше эпохи UNIX, начинающейся с 1 января 1970 года, на 2208988800 секунд), а также 32-битного поля для передачи долей секунды (перевод данного значения в микросекунды может быть осуществлен путем его деления на 4295). Можно предположить, что в 2036 году протокол потеряет свою актуальность, но это не так, ведь протокол предусматривает необязательную возможность передачи текущей метки времени (в поле времени отправки) на сервер вместе с метаданными, что позволяет серверу передвинуть начало эпохи NTP (например, следующая эпоха NTP начнется в 6 часов 28 минут и 16 секунд 7 февраля 2036 года). Для простоты рассматриваемое приложение может корректно функционировать лишь в пределах текущей эпохи NTP, но отправляет текущую метку времени на сервер. Протокол не предусматривает использования часовых поясов, поэтому на сервер могут передаваться как метки времени GMT, так и текущие метки времени. Для меток времени, как и для всех полей дейтаграммы, используется сетевой порядок следования байт (big-endian), следовательно как перед отправкой данных, так и после их приема должны осуществляться коррекции порядка следования байт с помощью таких реализованные в рамках библиотеки GLib макросов, как GUINT32_TO_BE() и GUINT32_FROM_BE().

Перед отправкой дейтаграммы клиентское приложение должно в обязательном порядке заполнить поле команд и информации и по возможности поместить текущую метку времени в поле времени отправки. Поле команд и информации является первым 4-х байтовым полем дейтаграммы и содержит множество полей, назначение которых стоит обсудить. Большинство полей заполняется данными при отправке дейтаграммы сервером. При разработке клиентского приложения наиболее важным является первый байт, содержащий три поля длиной в 2, 3 и 3 бита соответственно. Индикатор коррекции ("ИК") позволяет указать серверу на предстоящую операцию коррекции времени. Допустимые значения приведены в Таблице 6.

Таблица 6 - Допустимые значения индикатора коррекции

Значение Описание
0 Без коррекции времени
1 Последняя минута будет иметь 61 секунду
2 Последняя минута будет иметь 59 секунд
3 Время на синхронизировано

В рассматриваемом приложении не будет осуществляться коррекция времени (значение индикатора коррекции будет равно 0). С помощью следующих трех бит задается номер версии протокола ("НВ"), с которой работает клиент. Несмотря на то, что допустимо использование версий протокола с 1 по 4, для лучшей совместимости в рассматриваемом приложении будет использоваться версия 1. Поле режима работы приложения ("Режим") занимает последние три бита первого байта. Хотя и существует множество идентификаторов режимов работы приложений, в случае разработки клиентского приложения должен использоваться исключительно идентификатор 3 (режим клиента). Допустимые идентификаторы режимов работы приложения приведены в Таблице 7.

Таблица 7 - Допустимые идентификаторы режимов работы приложения

Идентификатор Описание
0 Зарезервировано
1 Симметричный активный режим
2 Симметричный пассивный режим
3 Режим клиента
4 Режим сервера
5 Режим широковещательной передачи
6 Зарезервировано для управляющих сообщений NTP
7 Зарезервировано для частного использования

Следующие три байта предназначены для передачи информации о параметрах источника времени сервера NTP и не заполняются клиентом. В поле "Страта" помещается значение, соответствующее уровню источника времени. В поле Интервал опроса - значение, двоичная экспонента которого соответствует минимальному времени между отправкой двух пакетов сервером. В поле Точность - значение, двоичная экспонента которого соответствует точности системных часов.

Следующие три 4-х байтовых поля также заполняются сервером. Поле "Задержка обращения" содержит значение, соответствующее времени, необходимому для доставки информации до сервера и обратно. Поле "Относительная ошибка часов сервера" - значение, показывающее максимальную ошибку, вызванную нестабильностью часов. Поле "Идентификатор источника" - 4-х байтовую ASCII-строку с указанием типа источника (ATOM - атомные часы, GPS - устройство GPS, и.т.д.) для первичных источников времени (значение в поле "Страта" равно 1) или 4 байта IP-адреса для вторичных источников.

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

Код рассматриваемого приложения очень похож на код предыдущего приложения, работающего с протоколом TCP, но ввиду того, что в данном случае используется протокол UDP, в коде отсутствует вызов метода g_socket_connect() для соединения с сервером, а методы g_socket_send() и g_socket_receive() заменены на методы g_socket_send_to() и g_socket_receive_from() соответственно, причем в данном случае ничто не мешает использовать метод g_socket_connect() для ассоциации адреса сервера с сетевым сокетом и методы g_socket_send() и g_socket_receive() для отправки и приема дейтагараммы. Объявление структуры, соответствующей пакету протокола SNTP, снабжено атрибутом packed, позволяющим избежать выравнивания полей структуры с появлением неиспользуемых областей. Непосредственный процесс синхронизации времени осуществляется в рамках функций application_start_sync_time(), application_resolver_lookup_callback() и application_read_callback(), причем первая функция предназначена для инициирования разрешения доменного имени, соотвествующего группе серверов NTP, вторая - для формирования и отправки запроса серверу NTP, а третья - для получения ответа от сервера NTP и его обработки. Обновление значений времени происходит с помощью таймера, осуществляющего периодический вызов функции application_upadate_time(), который создается в рамках функции application_activate().

Полный код рассматриваемого приложения приведен в Листинге 2, а также в архиве gsntp вместе с файлом Makefile для упрощения сборки. Для лучшего понимания в коде приложения используются комментарии. После распаковки архива исходный код может быть скомпилирован либо с помощью команды make, либо с помощью следующей команды:

gcc `pkg-config --cflags --libs gtk+-3.0` `pkg-config --cflags --libs gio-2.0` gsntp.c -o gsntp

При необходимости использованные в приложении низкоуровневые функции для работы с сетью могут быть применены и в не использующем GTK+ консольном приложении, которое может компилироваться с помощью следующей команды:

gcc `pkg-config --cflags --libs glib-2.0` `pkg-config --cflags --libs gio-2.0` <имя файла исходного кода> -o <имя исполняемого файла>

Листинг 2 - Код приложения gsntp

На Рисунке 5 представлен снимок окна приложения gsntp после синхронизации времени с сервером из Европы.

Окно приложения gsntp
Рисунок 5 - Окно приложения gsntp

Заключение

Несмотря на возможность использования системного API сокетов, в приложениях на основе библиотек GLib/GIO и тулкита GTK+ предпочтительнее использовать низкоуровневые функции для работы с сетью, реализованные в рамках библиотеки GIO. Преимуществами использования данных функций являются: упрощение кода (например, гораздо проще устанавливать ограничение времени выполнения операций с сетевым сокетом), упрощение интеграции с главным циклом обработки событий приложения, а также упрощение переноса приложений на другие платформы (главным образом Windows и Mac OS). Кроме того, имена методов объекта сетевого сокета GSocket схожи с именами функций сетевого API сокетов, поэтому вы можете использовать существующую литературу, посвященную разработке сетевых приложений, для разработки собственных приложений на основе библиотек GLib/GIO.


К началу серии статей : Возможности тулкита GTK+ и сопутствующих библиотек.