Как проверить состояние сокета firebird classic socket

Обновлено: 03.07.2024

Вы можете использовать другой порт, другое имя сервиса порта или и то, и другое. Вам может понадобиться это сделать, если порт 3050 требуется для другого сервиса, например, если параллельно используемый gds_db сконфигурирован для другой версии Firebird или для сервера InterBase. Есть несколько способов перекрыть значения по умолчанию. Сервер и клиенты оба должны быть сконфигурированы для изменения имени сервиса или номера порта (или и того, и другого) по меньшей мере одним из следующих способов:

* в строке соединения клиента;

* в команде запуска исполняемой программы сервера;

* активацией параметров RemoteServicePort или RemoteServiceName В firebird.config (версия 1.5 и выше);

* в демоне конфигурации (для Классического сервера в POSIX);

* в записи файла services.

Прежде чем проверять каждую из этих техник, будет полезным взглянуть на логику, используемую сервером для установки прослушиваемого порта, и порядок установки клиентом используемого порта.

Как сервер устанавливает прослушиваемый порт

Исполняемая программа сервера имеет необязательный переключатель в командной строке (-р), который можно использовать для указания номера порта или имени сервиса порта, который будет прослушиваться сервером. Если присутствует этот переключатель, то номер порта 3050 или имя сервиса порта (gds_db) будут заменены на значение аргумента, указанного в переключателе -р.

Затем- или вначале, если не установлен переключатель -р,- сервер версии 1.5 проверяет наличие параметров RemoteServicePort и RemoteServiceName В firebird.config.

* Если RemoteServiceName (но не RemoteServicePort) не закомментирован, то имя сервиса порта заменяется, только если это имя не было уже перекрыто переключателем -р.

* Если RemoteServicePort и RemoteServiceName не закомментированы, ТО RemoteServiceName получает приоритет, если он не был перекрыт аргументом -р. Если же имя сервиса порта уже было изменено, то значение RemoteServiceName будет проигнорировано, и новое значение RemoteServicePort заменит предыдущее значение 3050.

* В этой точке, если замена номера порта или имени сервиса была выполнена, то оба сервера версий 1.0 и 1.5 выполняют проверку файла services на наличие записи с корректной комбинацией имени сервиса и номера порта. Если соответствие найдено, то все в порядке. Если нет, и имя сервиса порта не gds db, то сервер выдаст исключение и отменит запуск. Если имя сервиса порта gds db и ему не может быть назначен никакой другой порт, он будет использовать порт 3050 автоматически.

Если значение по умолчанию для номера порта или имени сервиса было изменено, то вам может понадобиться создать запись в файле services. Для понимания того, нужно ли это делать, выполните шаги, описанные далее в этой главе в разд. "Конфигурирование файла services".

Использование переключателя -p

Запуск сервера с необязательным переключателем -р дает вам возможность перекрывать значение по умолчанию для номера порта (3050) или имя сервиса порта (gds db), которые используются сервером для прослушивания запросов на соединение. Переключатель может перекрывать одно значение, но не оба. В Firebird 1.5 и следующих вы можете использовать переключатель -р в комбинации с конфигурацией в файле firebird.conf для получения возможности перекрывать и номер порта, и имя сервиса порта.

Синтаксис для TCP/IP

Шаблон синтаксиса для команд:

Команда-сервера <другие переключатели< -р номер-порта j имя-сервиса

Например, для запуска Суперсервера как приложения и замены имени сервиса с gds_db на fb_db введите:

fbserver -а -р fb_db

Для замены порта 3050 на 3051 введите:

fbserver -а -р 3051

Синтаксис для Wnet

Для сети Wnet замените аргумент переключателя -р на обратная черта-обратная черта-точка-@:

fbserver -а -р \.@fb_db

fbserver -а -р \.@3051

Классический сервер в POSIX: демон inetd или xinetd

В Классическом сервере Firebird для Linux или UNIX демон inetd или xinetd сконфигурирован на прослушивание порта по умолчанию и имя сервиса по умолчанию. Инсталляционный скрипт запишет соответствующую запись в файл конфигурации /etc/inetd.conf или /etc/xinetd.conf.

Проблемы с подключением к Классическому серверу часто происходят по причине отсутствия или неправильной записи сервиса порта в этом файле. Вы можете проверить текущую запись, открыв файл в текстовом редакторе (например, vim) и скорректировав ее при необходимости. Следующий пример показывает, что вы должны увидеть в файле /etc/inetd.conf или /etc/xinetd.conf после инсталляции Классического сервера Firebird в Linux:

flags = REUSE KEEPALIVE

Если вы изменили сервис порта на значение, отличное от значения по умолчанию, вы должны соответственно изменить /etc/inetd.conf или /etc/xinetd.conf. Заново стартуйте xinetd (или inetd) с аргументом kill -HUP, чтобы убедиться, что демон будет использовать новую конфигурацию.

ВНИМАНИЕ! Запросы на соединения будут неуспешными, если xinetd (или inetd) и fbserver (или ibserver) оба пытаются прослушивать один и тот же порт. Если ваша хост-машина имеет подобную дублирующую конфигурацию, то необходимо сделать такие установки, при которых каждая версия сервера имела бы свой собственный порт.

Использование параметров файла конфигурации

В Firebird 1.5 и выше вы можете конфигурировать или RemoteServiceName, или RemoteServicePort в файле firebird.config для изменения номера порта по умолчанию (3050), или имени сервиса порта по умолчанию (gds_db), которые использует сервер для прослушивания запросов на соединение.

Сервер будет использовать один параметр Remoteservice*, но не оба. Если вы сконфигурировали оба, то он будет игнорировать RemoteServicePort во всех ситуациях, за исключением того случая, когда команда запуска сервера была вызвана с переключателем -р, перекрывающим имя сервиса порта. Следовательно, вы можете использовать в комбинации переключатель -р и параметр Remoteservice* для изменения номера порта и имени сервиса.

Если значения по умолчанию для номера порта или имени сервиса были изменены, то вам нужно создать запись в файле services.

ВНИМАНИЕ! Если вы не закомментировали ни RemoteServiceName, ни RemoteServicePort, но оставили значения по умолчанию нетронутыми, они будут трактоваться как измененные. Необходимо создать запись в файле services для установок значений по умолчанию сервиса порта.

Ожидание порта завершения ввода/вывода

Ожидание порта завершения ввода/вывода Для выполнения ввода/вывода с участием дескрипторов, связанных с портом, используются функции ReadFile и WriteFile со структурами OVERLAPPED (дескрипторы событий не требуются). Далее операция ввода/вывода помещается в очередь порта

9.5.1. Безопасность сервиса

9.5.1. Безопасность сервиса Когда я впервые знакомился с документацией на squid, то мне очень понравились следующие два параметра: cache_effective_user и cache_effective_group. Если squid запущен от имени администратора root, то идентификаторы пользователя и группы будут заменены на указанные в этих

11.4. Настройка DNS-сервиса

11.4. Настройка DNS-сервиса В настоящее время наиболее распространенным сервисом DNS для Linux является bind. Для этого сервиса существует программа bindconf, которая имеет графический интерфейс и проста в использовании. Зайдите в графическую оболочку и в консоли выполните

Тип сервиса

Тип сервиса В IP-пакетах предусмотрено специальное поле под названием TOS (Type-of-Service — тип сервиса). Это поле позволяет компонентам сети определять, какие из пакетов требуют специальной обработки. В результате подобной обработки для некоторых клиентов и серверов

Качество сервиса

Качество сервиса В большинстве случаев средства маршрутизации Linux обрабатывают пакеты по принципу "первый пришёл/первый обработан" (first-come/first-served). Такая процедура дает хорошие результаты в тех случаях, когда пропускная способность линий достаточна для передачи всех

9.9. Zip-диск фирмы Iomega для параллельного порта

9.9. Zip-диск фирмы Iomega для параллельного порта Для того, чтобы использовать Zip-дисковод, подключаемый к параллельному порту, вы можете использовать драйвер ppa, скомпилированный либо в составе ядра, либо в виде отдельного модуля. В последнем случае необходимо либо добавить

Изменение порта программы-сервера

Изменение порта программы-сервера Вы можете использовать порт по умолчанию, т. е. 4899, а можете «пересадить» программу-сервер на другой порт. В этом случае вам в диалоговом окне опций (см. рис. 8.27) следует убрать флажок Use default port (Использовать порт по умолчанию) и ввести

Компоненты Web-сервиса XML

Компоненты Web-сервиса XML В дополнение к библиотеке управляемого программного кода, обеспечивающей предлагаемые сервисом функциональные возможности, для Web-сервиса XML требуется определенная инфраструктура поддержки. В частности, Web-сервис XML использует следующие базовые

Использование порта

Использование порта Существуют две операции, которые могут выполняться с помощью порта: микропроцессор 8088 может послать информацию в подсоединенное устройство или прочитать данные из него. В языке Асемблера эти операции выполняются при помощи команд OUT и IN, а в

Функции вода/вывода с консольного терминала и порта

Функции вода/вывода с консольного терминала и порта Функции ввода/вывода для консольного терминала выделены в отдельную группу, потому что они используют специфические особенности компьютера IBM/PC (наличие специального видеоадаптера) и не являются переносимыми на

Установка клиента для поиска порта сервиса

Установка клиента для поиска порта сервиса Если вы установили ваш сервер с инсталляционными значениями по умолчанию (сервис gds db прослушивает порт 3050), то конфигурирование не требуется. Если сервер прослушивает другой порт или используется другое имя сервиса порта, то

ОПЫТЫ: Использование COM-порта в КПК серии iPAQ

ОПЫТЫ: Использование COM-порта в КПК серии iPAQ Автор: Александр ЯковлевМногие карманные компьютеры iPAQ фирмы Hewlett-Packard, например модель hx4700, имеют порт RS-232C. Однако в руководстве пользователя об этом ничего не сказано, так что о наличии порта знают не все. А ведь он позволяет

нельзя ли как нить узнать открыт ли сокет (т.е. установлено ли соединение) с клиентом со стороны сервера ?

Но так что бы не посылать что нибудь в сокет и смотреть код ошибки.

использую gcc, пишу под линукс

>нельзя ли как нить узнать открыт ли сокет (т.е. установлено ли соединение)
>с клиентом со стороны сервера ?
>
>Но так что бы не посылать что нибудь в сокет и смотреть
>код ошибки.
>
>использую gcc, пишу под линукс

Перебираем весь пул сокетов, делаем им getpeername/getsockname. Тока если есть пул, и он постоянно в обработке (poll/select) то и так должно быть ясно, кто подконнекчен и жив ли он. ИМХО, есть смысл задуматься над архитектурой ПО

>Перебираем весь пул сокетов, делаем им getpeername/getsockname. Тока если есть пул, и
>он постоянно в обработке (poll/select) то и так должно быть ясно,
>кто подконнекчен и жив ли он. ИМХО, есть смысл задуматься над
>архитектурой ПО

Я для каждого клиента создаю тред и там его обрабатываю (жду данных от клиента select+recv) при получении данных соответствующе отвечаю (send). Важно что б с одним клиентом было одно соединение и если вдруг оборвалась связь с клиентом (аппаратно, например обрыв кабеля) я должен об этом узнать и закрыть сокет, ожидая клиента вновь. Клиент может посылать данные очень редко, но на связи должен быть всегда. Пинговать его жуть как неохота, вот и хотелось бы отслеживать состояние сокета без записи/чтения данных в этот сокет (есть сокет == есть соединение с клиентом).

Так если создать массивчик дескрипторов открытых сокетов и юзать getpeername/getsockname должно все получиться?

>хотелось бы отслеживать состояние сокета без записи/чтения
>данных в этот сокет (есть сокет == есть соединение с клиентом).

А, интересно, почему Вам так не хочется записывать в сокет? Ну вернёт он EPIPE, ну обработаете Вы его. Чего сташного, я не понимаю.
А для постоянного отслеживания состояния клиента есть SO_KEEPALIVE, который генерит тот-же SIGPIPE. Да ещё есть SO_RCVTIMEO.


>>Перебираем весь пул сокетов, делаем им getpeername/getsockname. Тока если есть пул, и
>>он постоянно в обработке (poll/select) то и так должно быть ясно,
>>кто подконнекчен и жив ли он. ИМХО, есть смысл задуматься над
>>архитектурой ПО
>
>Я для каждого клиента создаю тред и там его обрабатываю (жду данных
>от клиента select+recv) при получении данных соответствующе отвечаю (send). Важно что
>б с одним клиентом было одно соединение и если вдруг оборвалась
>связь с клиентом (аппаратно, например обрыв кабеля) я должен об этом
>узнать и закрыть сокет, ожидая клиента вновь. Клиент может посылать данные
>очень редко, но на связи должен быть всегда. Пинговать его жуть
>как неохота, вот и хотелось бы отслеживать состояние сокета без записи/чтения
>данных в этот сокет (есть сокет == есть соединение с клиентом).
>
>
>Так если создать массивчик дескрипторов открытых сокетов и юзать getpeername/getsockname должно все
>получиться?

Нет, это не совсем так.
Во первых, сразу несколько полезных замечаний, по личному опыту проектирования пром-серверов. Лучше юзать poll вместо select. Непонятно, зачем вам создавать тред+демультиплексор для каждого клиента? Почему не загнать все сокеты в один демультиплексор, и надежнее и гемору с тредами, блокировками, синхрами, etc меньше.
Теперь к вопросу о доступности клиента. Пинговать его явно необязательно, ICMP составляющая и так отрабатывает в tcp-стеке, и если он порастет по тем или иным причинам, то на сокете поднимется флаг ошибки, а вы его сможете получить, через except-set в select'е или POLLERR/POLLHUP-event в poll'е. Правда стандартное время реакции ядра базового дистра на это событие около 3х минут, что нервирует, но эта трабла решается с помощью setsockopt.

поясню ситуацию: есть кучка удаленных аппаратов в которых вроли сигнализации используется GPRS-модем. Требуется всегда знать что аппарат на месте и оперативно узнать если сработал какой либо из датчиков.

Если сокет открыт (а именно установлен канал связи), значит аппарат наместе. Т.к. установлен ли канал связи нельзя проверить (?) ничего не послав в него приходится аппараты постоянно "пинговать" (имел ввиду не ping 1.2.3.4, а переодическую отправку данных в сокет и смотреть результат)

>Непонятно, зачем вам создавать тред+демультиплексор для каждого
>клиента?
select здесь использовал как удобное средство для одновременного обратного отсчета (т.е. аппарат не подовал признаков жизни t секунд значит пора бить тревогу) и собственно ожидания не придет ли что с аппарата.
а тред использовал из-за опасений вдруг что пойдет не так и как бы прога не заблокировалась при обработке одного клиента. тут ядро само разрулит и даст право на жизнь другим клиентам


>Почему не загнать все сокеты в один демультиплексор, и надежнее
>и гемору с тредами, блокировками, синхрами, etc меньше.
возможно все так и придется сделать


>Теперь к вопросу о доступности клиента. Пинговать его явно необязательно, ICMP составляющая
>и так отрабатывает в tcp-стеке, и если он порастет по тем
>или иным причинам, то на сокете поднимется флаг ошибки, а вы
>его сможете получить, через except-set в select'е или POLLERR/POLLHUP-event в poll'е.
но для этого все равно придется чтонибудь в сокет заслать как я понимаю? не ждать же SO_KEEPALIVE.

>>Теперь к вопросу о доступности клиента. Пинговать его явно необязательно, ICMP составляющая
>>и так отрабатывает в tcp-стеке, и если он порастет по тем
>>или иным причинам, то на сокете поднимется флаг ошибки, а вы
>>его сможете получить, через except-set в select'е или POLLERR/POLLHUP-event в poll'е.
>но для этого все равно придется чтонибудь в сокет заслать как я
>понимаю? не ждать же SO_KEEPALIVE.

Отчего же не ждать? Выставить его в нужное значение посредством setsockopt и ждать.

>>не ждать же SO_KEEPALIVE.

>Отчего же не ждать? Выставить его в нужное значение посредством setsockopt и
>ждать.

да вот как то смутила фраза из умной книжки о том, чтааа параметр этот нельзя назначить для отдельного сокета, а изменяется он чуть ли не перекомпиляцией ядра и действует на всю систему, что не есть хорошо.
Быть может книжка то не умная, а "умная"? Как оно в реальной жизни под линуксом? и если есть возможность, то с примерчиком.

>Быть может книжка то не умная, а "умная"? Как оно в реальной
>жизни под линуксом? и если есть возможность, то с примерчиком.
Книжка умная, но не очень свежая, наверное :-)

поищи на тему TCP_KEEPIDLE и TCP_KEEPINTVL, примеров, извини, не приведу.

>поясню ситуацию: есть кучка удаленных аппаратов в которых вроли сигнализации используется GPRS-модем.
>Требуется всегда знать что аппарат на месте и оперативно узнать если
>сработал какой либо из датчиков.
>
>Если сокет открыт (а именно установлен канал связи), значит аппарат наместе. Т.к.
Сокет - это файловый дескриптор, который ничего не знает о соединении. Сама структура стека TCP/IP не позволяет оперативно узнать об обрыве связи (отключении клиента). Это позволяют только сети с установлением соединения, например, X.25.

>установлен ли канал связи нельзя проверить (?) ничего не послав в
>него приходится аппараты постоянно "пинговать" (имел ввиду не ping 1.2.3.4, а
>переодическую отправку данных в сокет и смотреть результат)
>
Проще в данном случае использовать UDP, по которому каждый аппарат будет с заданной переодичностью (зависит от скорости канала и количества аппаратов) передавать свой уникальный номер и какую-либо временную отметку (простой счетчик).
Сервер на один открытый сокет принимает UDP пакеты от всех аппаратов и по их идентификаторам судит об их состоянии.
Как аппараты будут узнавать адрес/порт сервера зависит от протокола, примеры см. в SIP/RTP и прочих H.323.

>>Если сокет открыт (а именно установлен канал связи), значит аппарат наместе. Т.к.
>Сокет - это файловый дескриптор, который ничего не знает о соединении. Сама
>структура стека TCP/IP не позволяет оперативно узнать об обрыве связи (отключении
>клиента). Это позволяют только сети с установлением соединения, например, X.25.

А как же, позвольте, TCP/IP это протокол без установления соединения?

В системах, предназначенных для работы в реальном времени или близком к нему, существует проблема отслеживания на серверной стороне состояния клиентских соединений и принятия мер для их принудительного отключения в случае недоступности клиента вследствие разрыва соединения. Особенно при использовании Classic Firebird SQL Server важно своевременно освобождать ресурсы, занимаемые такими фантомными соединениями.

Если имеются пользователи, подключающиеся к серверу через модемы, то достаточно велика вероятность обрыва модемного соединения в самый неподходящий момент.

Точно так же обрывы соединений могут возникать и в локальной сети, если сбоит оборудование – сетевые карты, хабы, коммутаторы – или возникают помехи. В interbase.log/firebird.log обрывы коннектов tcp показываются как ошибки 10054 (Windows. на Unix – 104), обрывы netbeui – как ошибки 108/109.

Для отслеживания и отключения таких «мертвых» соединений InterBase и Firebird использует один из двух механизмов – DUMMY-пакеты (реализован на прикладном уровне начиная с InterBase 5.0 между сервером InterBase/ Firebird и клиентской библиотекой gds32/fbclient, включается в ibconfig/firebird.conf и в данном документе рассматриваться не будет) и KEEPALIVE-сокеты (используется по умолчанию начиная с InterBase 6.0). Использование KEEPALIVE включается установкой опции сокета SO_ KEEPALIVE при его открытии. Вам не нужно специально заботиться об этом, если вы используете Firebird 1.5 или выше – это реализовано в программном коде сервера Firebird, как для Classic, так и для Superserver. Для InterBase и Firebird (младше 1.5) в варианте Classic (существуют только для Unix/ Linux) необходима дополнительная настройка (см. п. 3). В этом случае отслеживание состояния соединения возлагается не на сервер Firebird, а на стек TCP операционной системы. Однако для практического использования требуется настройка параметров KEEPALIVE.

Примечание. Как показывает практика, устойчивость работы механизма dummy-пакетов, реализованная еще в InterBase 5.0 и неоднократно исправленная в Firebird 1.5. x сильно зависит от операционных систем клиента и сервера, версий стека tcp и множества других условий. То есть, эффективность такой системы в реальной сети стремится к нулю. Ко всему прочему, в Borland Developer Network упоминалось, что для Windows существует проблема с утечкой памяти в adf.sys при использовании dummy-пакетов. Именно поэтому необходимо настраивать механизм KEEPALIVE, за который отвечает стек tcp клиента и сервера.

Описание KEEPALIVE

Поведение KEEPALIVE-сокетов регулируется параметрами, представленными в таблице:

Параметр Описание
KEEPALIVE_ TIME Интервал времени, по истечении которого начинаются пробы KEEPALIVE
KEEPALIVE_INTERVAL Интервал времени между пробами KEEPALIVE
KEEPALIVE_PROBES Количество проб KEEPALIVE
Стек TCP отслеживает момент прекращения прохождения пакетов между клиентом и сервером, запуская таймер KEEPALIVE. Как только таймер достигнет величины KEEPALIVE_ TIME, стек TCP сервера выполняет первую пробу KEEPALIVE. Проба – это пустой пакет c флагом ACK, отправляемый клиенту. Если на стороне клиента все в порядке, то стек TCP на клиентской стороне посылает ответный пакет с флагом ACK и стек TCP сервера, получив ответ, сбрасывает таймер KEEPALIVE. Если клиент не отвечает на пробу, то пробы со стороны сервера продолжают выполняться. Их количество равно KEEPALIVE_ PROBES и выполняются они через интервал времени KEEPALIVE_ INTERVAL. Если клиент не ответил на последнюю пробу, то по истечении еще одного интервала времени KEEPALIVE_ INTERVAL стек TCP операционной системы сервера закрывает соединение и Firebird высвобождает все ресурсы, занимаемые обслуживанием данного соединения.

Таким образом, разорванное клиентское соединение будет закрыто по истечении времени KEEPALIVE_ TIME+ ( KEEPALIVE_ PROBES+1)* KEEPALIVE_ INTERVAL.

Значения параметров по умолчанию достаточно велики, что делает их практическое применение неэффективным. Параметр KEEPALIVE_ TIME, например, имеет значение по умолчанию 2 часа и в Linux и в Windows. Реально достаточно одной-двух минут для принятия решения о принудительном отключении недоступного клиента. С другой стороны, настройки KEEPALIVE по умолчанию иногда приводят к принудительному обрыву соединений в сетях Windows, которые неактивны в течение этих самых двух часов (сомнения по поводу необходимости наличия в приложениях таких соединений – это уже другой вопрос).

Ниже мы рассмотрим настройку этих параметров для операционных систем семейства Windows и операционной системы Linux.

Настройка KEEPAILVE в Linux

Параметры KEEPALIVE в Linux можно изменить либо прямым редактированием файловой системы / proc либо вызовами sysctl.

Для первого случая надо редактировать:

/proc/sys/net/ipv4/tcp_keepalive_time
/proc/sys/net/ipv4/tcp_keepalive_intvl
/proc/sys/net/ipv4/tcp_keepalive_probes sysctl –w net.ipv4.tcp_keepalive_time=value
sysctl –w net.ipv4.tcp_keepalive_intvl=value
sysctl –w net.ipv4.tcp_keepalive_probes=value

Время задается в секундах.

Для автоматической установки этих параметров в случае перезагрузки сервера добавьте в /etc/sysctl.conf:

net.ipv4.tcp_keepalive_intvl = value
net.ipv4.tcp_keepalive_time = value
net.ipv4.tcp_keepalive_probes = value

Слово <value> замените на нужные вам величины.

Если вы используете Firebird Classic ранее версии 1.5, то в /etc/xinet.d/firebird пропишите следующее:

Настройка KEEPALIVE в Windows 95/98/ME

HKEY_ LOCAL_ MACHINE\System\CurrentControlSet\Services\VxD\MSTCP Тип: DWORD
Для Windows 98, тип STRING.
Определяет время неактивности соединения в миллисекундах по истечении которого начинаются KEEPALIVE-пробы. Значение по умолчанию – 2 часа (7200000).
  • KeepAliveInterval = 32-значное число
Тип: DWORD
Для Windows 98, тип STRING.
Определяет время в миллисекундах между повторами KEEPALIVE-проб. Как только истек интервал KeepAliveTime через каждый интервал времени KeepAliveInterval (в миллисекундах) посылаются KEEPALIVE-пробы максимальным количеством MaxDataRetries. Если ответ не придет, соединение закрывается. Значение по умолчанию 1 секунда (1000).
  • MaxDataRetries = 32-значное число
Тип: STRING
Определяет максимальное количество KEEPALIVE-проб. Значение по умолчанию 5.

Настройка KEEPALIVE в Windows 2000/NT/XP

HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\Tcpip\Parameters\.


Настройка KEEPALIVE в Windows (для клиентов)

HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\Tcpip\Parameters
  • Убедимся, что в firebird.conf отключен механизм DUMMY-пакетов (параметр закомментирован)
  • Убедимся в наличии конфигурационного файла /etc/xinet.d/firebird
В нем все оставляем по умолчанию, как прописано при установке. Ничего добавлять не надо. sysctl –w net.ipv4.tcp_keepalive_time = 15
sysctl –w net.ipv4.tcp_keepalive_intvl = 10
sysctl –w net.ipv4.tcp_keepalive_probes = 5
  • Устанавливаем соединение к любой базе данных на сервере с любого сетевого клиента.
  • Смотрим трафик на сервере используя любой фильтр пакетов.
При указанной конфигурации параметров /proc/sys/net/tcp_ keepalive_* через 15 секунд с момента наступления тишины в канале сервером выполняется проба. Если клиент жив, то серверу высылается ответный пакет. Еще через 15 секунд проверка повторяется и т. д.
  • Если клиента отключить физически (выключить коммутатор или модем – мало ли, что может случиться в действительности), то на пробу сервера ответ от клиента не приходит, и сервер начинает с 10-ти секундным интервалом посылать пробы. Если на пятую пробу клиент не ответил, то еще через 10 секунд серверный процесс выгружается, освобождая ресурсы и блокировки. Если клиент подал признаки жизни и откликнулся хотя бы и на пятую пробу (худший случай), то снова выдерживается 15-сек тайм-аут и опять начинаются пробы. И т. д.

Заключение

В заключение хотелось бы привести практические рекомендации по выбору величин параметров KEEPALIVE.

Во-первых, определите для себя необходимую величину параметра KEEPALIVE_TIME. Чем больше будет его значение, тем позже начнутся KEEPALIVE-пробы. Если вы постоянно наблюдаете на своем сервере множество зависших коннектов, и вам приходится их удалять вручную, то следует уменьшить величину KEEPALIVE_TIME.

Во-вторых, значения параметров KEEPALIVE_INTERVAL и KEEPALIVE_PROBES должны удовлетворять вашим требованиям по своевременному отключению уже обнаруженных системой зависших соединений. Если ваши пользователи устанавливают соединения с сервером через ненадежные каналы связи, то вам, возможно, захочется увеличить количество проб и интервал между ними для того, чтобы пользователь успел обнаружить обрыв и восстановить соединение с сервером. В случае, если клиентоы используют выделенное подключение к сетям общего пользования (Интернет) или используют доступ к SQL-серверу по локальной сети, возможно уменьшение количества и интервала между KEEPALIVE-пробами.

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

В этой статье мы опишем минимальный набор действий, необходимых для оптимальной установки СУБД Firebird версии 3.0 на новые дистрибутивы Linux. Для примеров выбраны CentOS 8 и Ubuntu 19.

Для самых нетерпеливых — сразу в бой:

Быстрая установка

Редактируем файл /etc/sysctl.conf , добавляя строку:


Сохраняем файл и применяем настройку:


Дальнейшие инструкции различаются для CentOS 8 и Ubuntu 19, но ССЫЛКА и КАТАЛОГ обозначают ссылку с официального сайта проекта Firebird для загрузки дистрибутива и каталог, в который будет распакован дистрибутив в процессе загрузки.
На текущий момент (март 2020) актуален релиз Firebird 3.0.5 (вот ссылка на 64-битную версию).


Собственно установка СУБД Firebird:


Если вы хотите лучше понимать, что чего служат эти действия – читайте дальше.

Основная часть

Небольшая преамбула

Предполагается, что уже ОС установлена в минимальном варианте и настроен доступ к публичным репозиториям или к их локальным копиям.

Предполагается, что у читателя есть базовые знания о Linux и, СУБД Firebird.

Планирование

На сервере СУБД рекомендуется выделять отдельные разделы для временных файлов ( /tmp ), файлов баз данных и локальных бэкапов.

К временным относятся lock-файлы, файлы сортировок, файлы «материализации» глобальных временных таблиц (GTT) и таблиц мониторинга. Файлы сортировок и глобальных временных таблиц расположены в /tmp , файлы mon$-таблиц и lock-файлы – в /tmp/firebird .

Файлы сортировок «удаляются» ( unlink ) сразу после создания, поэтому их нельзя «увидеть» в листинге каталога – только в списке описателей (handles) процесса (помечены как deleted ):


В листинге псевдокаталога /proc/…/fd/ отображаются симлинки, а фактическую информацию о файле даёт:


где НОМЕР – описатель (дескриптор) интересующего файла.

Вместо вызова « pgrep исполняемый-файл » можно сразу подставить идентификатор интересующего процесса.

Временные файлы могут быть очень большими, поэтому для /tmp рекомендуется выделять не менее 20-30 ГБ. Следует учитывать, что размер файлов сортировок зависит только от объёма данных, явно или неявно сортируемых в запросе и один-единственный пользователь может «создать» гигабайты временных файлов.

Раздел для файлов баз данных должен вмещать файлы всех баз. плюс, как минимум, копию файла самой большой базы. Следует учитывать рост файлов баз в перспективе на несколько лет вперёд.

Раздел локальных бэкапов должен вмещать, как минимум, по одному архиву бэкапов всех баз плюс бэкап самой большой базы. Желательно, чтобы на этом разделе было и место для восстановления самой большой базы. Следует учитывать рост бэкапов и архивов бэкапов в перспективе на несколько лет вперёд.

Предварительная подготовка

Сервер СУБД Firebird 3.0 динамически выделяет и освобождает системную память, что может приводить к её фрагментации. Например, после единовременного отключения от суперсервера большого числа пользователей, могут возникают ошибки при новых подключениях.

Фрагментацию памяти контролирует системный параметр vm.max_map_count , по умолчанию – 64K. Рекомендуется увеличить его значение вчетверо:


Чтобы новое значение устанавливалось при перезагрузке системы, добавляем в файл /etc/sysctl.conf строчку:


Желательно сделать комментарий, чтобы была понятна причина изменения этого параметра. Можно сначала отредактировать файл, а затем применить сохранённые в нём установки:


Установка необходимых пакетов

Исполняемые файлы СУБД Firebird 3.0 Linux зависят от библиотек ncurses ( libncurses.so.5 ), ICU (без привязки к версии и без отображения в выводе ldd ) и tommath ( libtommath.so.0 ). Для загрузки и распаковки архива сборки потребуются утилиты gzip , tar и curl или wget . Версии ICU, gzip , tar и curl / wget – несущественны.

Работа с пакетами зависит от системы и от используемого в системе пакетного менеджера, поэтому рассмотрим их поочерёдно.

CentOS 8 использует новый пакетный менеджер – dnf и он же «прозрачно» вызывается по команде yum . Поскольку для наших целей между ними нет разницы – в примерах будет yum .

Обновляем кэш метаданных: sudo yum makecache

Пакет libtomath находится в отдельном E(xtra)P(ackages for)E(nterprise)L(inux) репозитории, поэтому проверяем, что он уже подключен:


Опция «только из кэша» ( -C или --cache-only ) используется, чтобы исключить ненужные проверки и загрузки, ускорив работу yum. Если в списке нет epel-репозитория – устанавливаем его и обновляем кэш метаданных:


Подтверждаем запросы, при необходимости сверяя значения pgp-ключей с уже известными из доверенного источника.

Проверяем статус нужных пакетов (команда сложена, в примере вывода отфильтрован 32-разрядный пакет):


Видим, что curl , gzip и ncurses размещены в псевдорепозитории установщика ( anaconda ), а tar – исключён из минимальной установки системы. Мажорные версии libncurses и libtommath больше, чем требуется: 6 и 1 вместо 5 и 0, соответственно. Если один и тот же пакет и установлен и доступен – для него выпущено обновление. Устанавливаем недостающие пакеты:

Для управления пакетами предназначены утилиты apt , apt‑get и apt‑cache . Первая рассчитана на интерактивную работу, а две последние – на использование в скриптах. Имена пакетов немного другие и включают версию.

Проверяем статус нужных пакетов (команда сложена, пример вывода сокращён и отфильтрованы 32-разрядные пакеты):


Пакеты, для которых в квадратных скобках указано installed / upgradable – установлены. Доступен, но не установлен ncurses5 , вместо curl установлен wget . Устанавливаем недостающие пакеты:

Поскольку libtommath.so.1 и libncurses.so.6 обратно совместимы с libtommath.so.0 и libncurses.so.5 , то для Firebird достаточно создать симлинки на имеющиеся версии библиотек.

Находим libtommath.so.1 ( libncurses.so.? расположены в этом же каталоге ):


Проверяем результат (команда сложена, примеры вывода сокращены):


Загрузка дистрибутива СУБД Firebird.

Официальные выпуски для линукса доступны в виде архивов (tar.gz) и пакетов deb/rpm, а сборки – только в виде архивов. Мы будем рассматривать «общий установщик» (generic installer из tar.gz).

Архив сборки требуется загрузить и распаковать, но мы совместим оба этих процесса. Распаковка делается в /tmp , URL обозначает ссылку на загружаемый архив.


По умолчанию curl посылает загружаемые данные на стандартный вывод, но не обрабатывает перенаправления и мы добавляем « ‑L », а wget , наоборот: обрабатывает перенаправления, но записывает данные в файл и мы ставим « ‑O‑ ». Для tar указываем использование gzip -фильтра и каталог, в который будет выполнена распаковка. По завершении процесса появится каталог вида Firebird‑3.0.5.33220‑0.amd64 с тремя файлами: install.sh , buildroot.tar.gz и manifest.txt .

Установка Firebird

В ходе предварительной подготовки мы отрегулировали значение системного параметра vm.max_map_count , проверили наличие и установили библиотеки ICU, ncurses и tommath. Убедились в правильности версий ncurses и tommath ( libncures.so.5 и libtommath.so.0 ) и создали необходимые симлинки.

Собственно установка делается очень просто. Переходим в каталог, куда был распакован архив дистрибутива Firebird, проверяем и, при необходимости, устанавливаем флаг «исполняемый» скрипту install.sh :


запускаем инсталяционный скрипт:


нажатием клавиши Enter подтверждаем начало установки, а по получении запроса – вводим пароль sysdba.

Скрипт установки автоматически запускает systemd -юнит firebird-superserver (умалчиваемая архитектура Firebird 3.0). Сервис Firebird будет работать с параметрами по умолчанию для суперсервера: страничный кэш в 2048 страниц (на базу), буфер сортировок в 64 МБ (общий) и подключение только клиентов третьей версии. Просмотр параметров firebird.conf :


Следует учитывать, что новые значения из firebird.conf будут активированы только после перезапуска сервиса Firebird.

При подборе значений параметров следует учитывать, что есть три основных «потребителя»: страничный кэш (для базы), буфер сортировок (общий) и память, выделенная сервером для клиентских подключений. Управлять можно только первыми двумя – объём памяти клиентских подключений зависит от количества и текста кэшированных запросов, их планов и задействованных в запросах объектов базы. Оценка памяти клиентских подключений делается только эмпирически и может меняться при изменении клиентских приложений и/или объектов базы.

Для суперсервера на хостах с малым объёмом памяти (до 12-16 ГБ) не следует выделять для страничного кэша и буфера сортировки более трети-четверти от общего объёма ОЗУ.

Если количество баз не фиксировано и может меняться – общий объём памяти страничного кэша следует делить на максимальное количество баз, которые могут быть на сервере. Размер страничного кэша задаётся в страницах и его надо отдельно пересчитывать в байты.

Для переключения на архитектуру классика требуется, как минимум, явно указать ServerMode в firebird.conf , уменьшить там же страничный кэш (не более 2K), уменьшить буфер сортировок (суммарный допустимый объём всех сортировок, поделённый на максимальное количество подключений), запретить и остановить юнит firebird-superserver , разрешить и запустить юнит firebird-classic.socket .

Использование архитектуры суперклассик в Firebird 3.0 не имеет особого смысла: «надёжность» – как у суперсервера и такой же общий буфер сортировок. Нет общего страничного кэша и «потери» на синхронизацию разных подключений между собой такие же, как у классика.

Репутация: нет
Всего: 3

Здравствуйте.
Знаю, что вопрос относительно тривиальный, но обращаюсь за помощью и что бы разобраться в ситуации.

Пишу под Linux. Работаю с сокетами. Использую "чистый" C.

Благополучно соединяюсь с ним, работаю, по завершению работы или при возникновении ошибок делаю его close(sock_descr).
Далее, в зависимости от ситуации, мне нужно повторно использовать sock_descr.
Либо повторно им воспользоваться, если я не делал ранее его close, либо заново сделать connect и пользоваться им.
Вопрос заключается в следующем:
как в определенном месте однозначно надежно (и по возможности просто) выяснить, что этот дескриптор сокета был ранее closed?

Решение этого вопроса гуглил, но в ходу не совсем очевидные мне методы выяснения статуса сокета. Рекомендуют использовать read/write, но у меня вызывает сомнение использование этого метода.
Адекватный ли вариант "мануально" контролировать статус сокета посредством использования рукописной структуры?
Пример:

Код

struct MySocket int socket_descriptor;
int status; //Статус сокета. Каждый раз при вызове close устанавливать его руками.
>

Репутация: 1
Всего: 45

Довольно странный подход. Если сокет был закрыт, то его дескриптор становится свободным, а память под внутренние структуры высвобождена. Как его можно использовать? Никак. К тому же любой последующий open/fopen/socket может занять этот дескриптор, и прощай надежды.


Опять-таки, после закрытия сокета его использовать никоим образом нельзя. Поэтому очень часто пишут так: Присвоение переменной сокета значения -1 как раз и будет указывать на то, что сокет был закрыт. Дополнительного значения status здесь не требуется. В дальнейшем в коде можно просто делать проверку на положительность значения переменной сокета: Напильник, велосипед, грабли и костыли - основные инструменты программиста.

Репутация: нет
Всего: 3

Перечитал свой первый пост, достаточно запутанно описал ситуацию.
По сути, в случае ошибки, мне нужно делать полное переподключение и я использую один и тот же дескриптор сокета.
Как выходить из этой ситуации?
Вручную присваивать дескриптору после close значение -1? Как в таком случае открывать этот дескриптор повторно?

Репутация: нет
Всего: 459

гениальность идеи состоит в том, что ее невозможно придумать

Репутация: 1
Всего: 45

В случае какой ошибки? Ошибки бывают слишком разные.


Зачем? В чем причина необходимости для использования того же дескриптора сокета? Почему бы просто не закрыть сокет, а потом его по-новой открыть в случае переподключения? У Вас им удерживается свой IP:port?
Напильник, велосипед, грабли и костыли - основные инструменты программиста.

Репутация: нет
Всего: 3

Alexeis, ООП использовать не могу - "чистый" Си.
feodorv, по поводу ошибок. Имеются ввиду ошибки при подключении или при обмене данными (хост недоступен, порт занят, сервер не возвращает данные). В каждом из этих случаев мне необходимо осуществить несколько попыток повторного подключения. Я принял во внимание ваши советы, пересмотрел код. Вопросы стали еще проще и еще банальнее:
1. Фактически close не изменяет значение дескриптора сокета, а лишь закрывает его. Верно? Т.е. после close я могу присвоить переменной в которой ранее хранился действующий дескриптор сокета отрицательное значение для индикации того, что сокет закрыт?
2. Дескриптор действующего сокета не может быть отрицательным. Может ли дескриптор сокета быть равным 0?

Репутация: 1
Всего: 45

По сути верно, по форме я бы иначе сказал. Дело в том, что переменная сокета хранит дескриптор сокета, и при close данный дескриптор высвобождается и более ничему не соответствует (ни файлу, ни сокету), хотя переменная сокета продолжает хранить значение этого уже освобождённого дескриптора. Чтобы она этого не делала, и для индикации закрытости сокета, стОит присвоить переменной сокета отрицательное значение (обычно используют -1).

Не может. Более того, отрицательное значение, возвращаемое вызовом socket(), сигнализирует об ошибке.


Вообще, значения дескрипторов 0, 1 и 2 отведены под stdin, stdout и stderr. Если их последовательно закрыть, а затем сделать вызов socket(), то вполне возможно получить нулевое значение дескриптора сокета. По крайней мере, так было в Юниксах лет 10 назад, к сожалению, меня с тех пор от них оторвали, я не в курсе последних тенденций в этом направлении. Но учитывая значительный консерватизм в разработке Юниксов, не думаю, что здесь что-то изменилось. Можно, кстати, поставить эксперимент. Но в любом случае по стандарту нужно быть готовым к тому, что значение дескриптора сокета может быть нулём)))
Напильник, велосипед, грабли и костыли - основные инструменты программиста.

Репутация: нет
Всего: 3

feodorv, большое спасибо! Вы все объяснили до очевидного понятно.
Ответы на все сформулированные вопросы я получил. Тему закрою если не возникнет дополнительных вопросов при исправлении взаимодействия с сокетами в коде.

Репутация: нет
Всего: 3

Помечаю вопрос как решенный.
Воспользовался советами feodorv.

Репутация: 4
Всего: 4

  • Ваш сервер установил ТСР соединение с клиентом
  • Идёт приём данных от клиента
  • Что-то случилось на сервере и он закрывает все сокеты и пытается перестартоваться
  • Но приём данных не завешён и может быть завершён - неизвестно когда. ОС не освобождает сетевой адрес этого соединения
  • При повторном открытии сокета и его биндинге возникает конфликт

Репутация: нет
Всего: 459


ОС закрывает соединение при возникновении любой неисправимой ошибки. Но это не имеет отношения к теме обсуждения. Автор спрашивал как проверить закрыт или открыт сокет, а не соединение. Сокет может быть открытым как при открытом так и при закрытом соединениях. Есть еще вариант проверки при помощи функции recv . После ожидания таймаута открытый сокет с открытым соединением вернет EAGAIN или EWOULDBLOCK. Закрытый сокет или в состоянии ошибки вернет код ошибки моментально. Но этот способ страдает таймаутом, так что не эффективен.

гениальность идеи состоит в том, что ее невозможно придумать

Если Вам понравилась атмосфера форума, заходите к нам чаще! С уважением, xvr.

[ Время генерации скрипта: 0.1270 ] [ Использовано запросов: 21 ] [ GZIP включён ]

Читайте также: