Итак, вы хотите иметь возможность перезагрузить программу, не теряя соединения с открытым сокетом?
Первое, что нужно понять, это то, что при выходе из процесса все дескрипторы открытых файлов закрываются.Это включает в себя сокетные соединения.Запуск в качестве демона не меняет этого.Процесс становится демоном, становясь независимым от вашей терминальной сессии, так что он будет продолжать работать после окончания вашей терминальной сессии.Но, как и любой другой процесс, когда демон по какой-либо причине завершает работу (обычный выход, сбой, сбой, перезапуск компьютера и т. Д.), Все соединения с ним перестают существовать.Кстати, это не относится к Unix, Windows - то же самое.
Итак, краткий ответ на ваш вопрос - НЕТ, нет никакого способа сказать unix / linux, чтобы он не закрывал ваши сокеты, когда ваш процесс останавливается, он будетзакройте их, вот и все.
Длинный ответ: есть несколько способов реорганизовать вещи, чтобы обойти это:
1) Вы можете иметь свою программу exec()
, когдаВы отправляете ему специальное сообщение или сигнал (например, SIGHUP
).В unix exec
(или несколько его вариантов) не завершает и не запускает какой-либо процесс, он просто загружает код в текущий процесс и начинает выполнение.Новый код заменяет старый в том же процессе.Поскольку процесс остается тем же, все открытые файлы остаются открытыми.Однако вы потеряете все данные, которые у вас были в памяти, поэтому сокеты будут открыты, но ваша программа ничего о них не узнает.При запуске вам придется использовать различные системные вызовы, чтобы узнать, какие дескрипторы открыты в вашем процессе и являются ли какие-либо из них сокетными соединениями с клиентами.Один из способов обойти это - передать критическую информацию в виде аргументов командной строки или переменных среды, которые можно передать через вызов exec()
и, таким образом, сохранить для использования нового кода, когда он начнет выполняться.
Keepпомните, что это работает только тогда, когда процесс вызывает exec
САМ, пока он еще работает.Таким образом, вы не можете восстановиться после сбоя или любой другой причины завершения вашего процесса ... ваши соединения будут разорваны.Но этот метод решает проблему того, что вы хотите загрузить новый код без потери соединений.
2) Вы можете обойти проблему, разделив свой сервер (мастер) на два процесса.Первый (назовите его «прокси») принимает TCP-соединения от клиентов и сохраняет их открытыми.Прокси никогда не может выйти, поэтому он должен быть настолько простым, что вам редко придется менять этот код.Второй процесс запускает «рабочий», то есть код, который реализует логику вашего приложения.Весь код, который вы, возможно, захотите часто менять, должен идти в рабочий.Теперь все, что вам нужно - это установить межпроцессное взаимодействие между прокси-сервером и работником и убедиться, что, если работник выходит, в прокси-сервере достаточно информации, чтобы восстановить состояние приложения при повторном запуске работника.В действительно простом приложении с небольшим объемом этот механизм может быть таким же простым, как прокси-сервер, выполняющий fork()
+ exec()
работника каждый раз, когда ему нужно что-то делать.Более причудливый способ сделать это, который я использовал с хорошими результатами, это сокет датаграммы домена Unix (SOCK_DGRAM
).Прокси-сервер получает сообщения от клиентов, пересылает их работнику через сокет дейтаграммы, работник выполняет работу и возвращает результат обратно прокси-серверу, который, в свою очередь, пересылает его обратно клиенту.Это работает хорошо, потому что, пока прокси-сервер работает и открыл сокет домена unix, работник может перезапускаться по желанию.Общая память также может использоваться для связи между прокси-сервером и работником.
3) Вы можете использовать сокет домена unix вместе с функциями sendmesg()
и recvmsg()
вместе с флагом SCM_RIGHTS
, чтобы передавать не сами данные клиента, а фактически отправлять дескрипторы файлов открытого сокета из старого экземпляр на новый. Это единственный способ передать дескрипторы открытых файлов между несвязанными процессами. Используя этот механизм, вы можете реализовать всевозможные стратегии ... например, вы можете запустить новый экземпляр вашей главной программы и подключить его (через сокет домена unix) к старому экземпляру и перенести все сокеты поверх , Тогда ваш старый экземпляр может выйти. Или вы можете использовать модель прокси / работник, но вместо передачи сообщений через прокси вы можете просто дать прокси передать дескриптор сокета работнику через сокет домена unix между ними, и тогда работник сможет напрямую общаться с клиент, использующий этот дескриптор. Или вы можете попросить своего мастера отправить все свои дескрипторы файлов сокетов другому процессу «stash», который их удерживает в случае, если мастер должен перезапуститься. Возможны все виды архитектур. Помня о том, что операционная система просто предоставляет возможность доставлять дескрипторы, всю остальную логику, которую вы должны кодировать для себя.
4) Вы можете принять, что независимо от того, насколько вы осторожны, неизбежно будут потеряны связи. Сети ненадежны, программы иногда зависают, машины перезагружаются. Поэтому вместо того, чтобы предпринимать значительные усилия, чтобы убедиться, что ваши соединения не закрываются, вы можете вместо этого спроектировать систему для восстановления, когда это неизбежно произойдет.
Простейшим подходом к этому будет следующее: поскольку ваши клиенты знают, к кому они хотят подключиться, ваши клиентские процессы могут запустить цикл, в котором, если по какой-либо причине соединение с мастером потеряно, они периодически пытаются повторно подключиться. (скажем, каждые 10-30 секунд), пока они не преуспеют. Таким образом, все, что нужно сделать мастеру, - это открыть сокет рандеву (прослушивания) и подождать, и соединения будут восстановлены с каждого работающего клиента. Затем клиент должен повторно отправить любую имеющуюся информацию, необходимую для восстановления надлежащего состояния в мастере.
Список подключенных компьютеров может храниться в памяти мастера, нет причин записывать его на диск или куда-либо еще, поскольку при выходе мастера (по любой причине) эти подключения больше не существуют. Затем любой клиент может подключиться к вашему серверу (мастеру) и запросить список подключенных клиентов.
Лично я бы выбрал этот последний подход. Поскольку в вашей системе кажется, что сами соединения гораздо более ценны, чем состояние мастера, возможность восстановить их в случае потери будет первым приоритетом.
В любом случае, поскольку кажется, что роль мастера состоит в том, чтобы просто передавать данные назад и вперед между клиентами, это было бы хорошим применением "асинхронного" сокетного ввода-вывода с использованием select()
или poll()
функции, это позволяет вам общаться между несколькими сокетами в одном процессе без блокировки. Вот хороший пример сервера на poll()
, который принимает несколько соединений:
https://www.ibm.com/support/knowledgecenter/ssw_ibm_i_71/rzab6/poll.htm
Что касается запуска вашего процесса "вне системы" .. в Unix / Linux это называется выполнением демона. В * ix эти процессы являются потомками процесса с идентификатором 1, процесса init
, который является первым процессом, который запускается при запуске системы. Вы не можете указать своему процессу стать потомком init
, это происходит автоматически при выходе из существующего родителя. Все "осиротевшие" процессы приняты init
. Поскольку существует множество легко встречающихся примеров написания демона unix (к этому моменту код, который вам нужно написать, чтобы сделать это, стал довольно стандартизированным), я не буду вставлять здесь какой-либо код, но вот один хороший пример, который я нашел: http://web.archive.org/web/20060603181849/http://www.linuxprofilm.com/articles/linux-daemon-howto.html#ss4.1
Если в вашем дистрибутиве Linux используется systemd
(недавняя замена init
в некоторых дистрибутивах), то вы можете сделать это как сервис systemd, который является идеей systemd о демоне, но они выполняют часть работы за вас. (к лучшему или к худшему .. есть много жалоб на systemd .. войны ведутся практически) ...