Завершение QWebSocketServer с подключенными сокетами
Я отлаживаю консольное многопоточное приложение, написанное на C++/Qt 5.12.1. Он работает на Linux Mint 18.3 x64.
Это приложение имеет SIGINT
обработчик, QWebSocketServer
а также QWebSocket
Таблица. Оно использует close()
QWebSocketServer
и позвонить abort()
/deleteLater()
для предметов в QWebSocket
стол для обработки прекращения.
Если клиент websocket подключается к этому консольному приложению, завершение завершается неудачно из-за какого-то работающего потока (я полагаю, он внутренний QWebSocket
нить). Завершение успешно, если не было никаких соединений.
Как это исправить? Так что приложение изящно выходит.
2 ответа
Чтобы корректно выйти из сокет-сервера, мы можем попытаться:
Самая важная часть заключается в том, чтобы позволить циклу событий основного потока запускаться и ожидать QWebSocketServer::closed(), чтобы слот вызывал QCoreApplication:: quit ().
Это можно сделать даже с помощью:
connect(webSocketServer, &QWebSocketServer::closed,
QCoreApplication::instance(), &QCoreApplication::quit);
Если нам не нужна более детальная реакция.
После подключения этого сигнала, прежде всего, перейдите к pauseAccepting()
чтобы предотвратить больше соединений. Вызов QWebSocketServer::close
,
Ниже может не понадобиться, если выше достаточно. Сначала вы должны попробовать описанное выше, и только если все еще есть проблемы, тогда работайте с существующими и ожидающими соединениями. Исходя из моего опыта, поведение было различным на платформах и с некоторыми уникальными реализациями websocket в серверной среде (что, вероятно, просто Qt для вас).
Пока у нас есть массив с QWebSocket
случаи, мы можем попытаться позвонить QWebSocket::abort()
на всех их немедленно выпустить. Этот шаг, похоже, описан автором вопроса.
Попробуйте перебрать ожидающие соединения с помощью QWebSocketServer::nextPendingConnection() и вызвать abort()
для них. Вызов deleteLater
, если это работает так же хорошо.
Не нужно ничего делать. Что вы подразумеваете под "изящным выходом"? Как только появится запрос на прекращение вашего приложения, вы должны немедленно прекратить его, используя exit(0)
или аналогичный механизм. Вот каким должен быть "изящный выход".
Примечание: я был преобразован. Раньше я думал, что изящные выходы - это хорошо. Чаще всего они являются пустой тратой ресурсов процессора и обычно указывают на проблемы в архитектуре приложения.
Хорошее обоснование того, почему это должно быть написано в kj
рамки (часть capnproto).
Цитируя Кентона Варда:
KJ_NORETURN(virtual void exit()) = 0;
Указывает на завершение программы. Программа считается успешной, если
error()
назывался. Как правило, это выходит с_Exit()
Это означает, что стек не разматывается, буферы не очищаются и т. д. - вызывающая сторона несет ответственность за очистку любых значимых буферов. Однако альтернативная реализация контекста, например, для целей модульного тестирования, может вместо этого выбрать исключение.Сначала такой подход может показаться сумасшедшим. Разве не лучше выключить чисто? Что если вы потеряете данные? Тем не менее, оказывается, что если вы посмотрите на каждый общий класс программы,
_Exit()
почти всегда предпочтительнее. Давайте разберемся с этим:
Команды: Типичная программа, которую вы можете запустить из командной строки, является однопоточной и быстро и детерминируется. Команды часто используют буферизованный ввод-вывод, и перед выходом необходимо очистить эти буферы. Однако большая часть работы, выполняемой деструкторами, заключается не в очистке буферов, а в освобождении памяти, размещении объектов в списках фрилансов и закрытии файловых дескрипторов. Все это не имеет значения, если процесс все равно собирается завершиться, и для быстро выполняющейся команды потраченное время, освобождая пространство кучи, может существенно изменить общее время выполнения сценария. Между тем, обычно легко точно определить, какие ресурсы необходимо очистить перед выходом, и легко определить, не сбрасываются ли они (поскольку команда не выдает ожидаемый результат). Поэтому командам достаточно просто явно убедиться, что все выходные данные сброшены перед выходом, и, вероятно, в любом случае это хорошая идея, поскольку ошибки записи должны быть обнаружены и обработаны. Для команд хорошей стратегией является выделение любых объектов, которые требуют чистого уничтожения в стеке, и разрешение им выйти из области видимости до выхода из команды. Между тем любые ресурсы, которые не нужно очищать, должны быть выделены как члены основного класса команды, чей деструктор обычно не вызывается.
Интерактивные приложения. Программы, которые взаимодействуют с пользователем (будь то графические приложения с окнами или консольные приложения, такие как emacs), обычно завершают работу только тогда, когда пользователь запрашивает их. Такие приложения могут хранить большие структуры данных в памяти, которые должны быть синхронизированы с диском, такие как документы или пользовательские настройки. Однако полагаться на разворачивание стека или глобальные деструкторы как механизм обеспечения такой синхронизации, вероятно, неправильно. Прежде всего, это 2013 год, и приложения должны активно синхронизировать изменения в энергонезависимой памяти в момент внесения этих изменений. Приложения могут аварийно завершить работу в любое время, и сбой никогда не должен приводить к потере данных, возраст которых превышает полсекунды. Между тем, если пользователь действительно пытается закрыть приложение, пока существуют несохраненные изменения, пользовательский интерфейс приложения должен предложить пользователю решить, что делать. Такой механизм пользовательского интерфейса явно слишком высок для реализации через деструкторы, поэтому KJ использует
_Exit()
не должно иметь значение здесь.Серверы: Хороший сервер является отказоустойчивым, готовым к тому, что в любой момент может произойти сбой, ОС может решить его отключить или машина, на которой он работает, может просто умереть. Таким образом, использование _Exit() не должно быть проблемой. Фактически, серверы вообще никогда даже не вызывают выход; они убиты внешне.
Пакетные задания: длительное пакетное задание - это нечто среднее между командой и сервером. Вероятно, он точно знает, что нужно очистить перед выходом, и, вероятно, должен быть отказоустойчивым.