Implementiere sanften Shutdown und verbessere Server-Stopp-Logik

- Füge Signalhandler in `main.cpp` hinzu, um einen sanften Shutdown bei SIGINT und SIGTERM zu ermöglichen.
- Aktualisiere die `Server`- und `SSLServer`-Klassen, um die Stopp-Logik zu verbessern und Threads ordnungsgemäß zu beenden.
- Optimiere die Timeout-Werte in der `run`-Methode für eine schnellere Reaktion auf Shutdown-Signale.
- Ergänze Logging für den Server-Stopp-Prozess zur besseren Nachverfolgbarkeit.
This commit is contained in:
Torsten Schulz (local)
2025-09-04 14:22:01 +02:00
parent c9235034b1
commit 8d0d1bc187
4 changed files with 118 additions and 17 deletions

View File

@@ -13,6 +13,8 @@
#include <arpa/inet.h> #include <arpa/inet.h>
#include <sstream> #include <sstream>
#include <regex> #include <regex>
#include <errno.h>
#include <cstring>
namespace Yc { namespace Yc {
namespace Lib { namespace Lib {
@@ -63,13 +65,33 @@ namespace Yc {
} }
} }
Server::~Server() {
stop();
if (_socket >= 0) {
close(_socket);
_socket = -1;
}
}
void Server::stop() {
std::cout << "[YourChat] Stopping regular server..." << std::endl;
_stop = true;
// Stop all rooms
for (auto& room : _rooms) {
room->setStop();
}
std::cout << "[YourChat] Regular server stopped." << std::endl;
}
void Server::run() { void Server::run() {
if (listen(_socket, 5) < 0) { if (listen(_socket, 5) < 0) {
std::cout << "listen not possible" << std::endl; std::cout << "listen not possible" << std::endl;
exit(-1); exit(-1);
} }
timeval origTv; timeval origTv;
origTv.tv_sec = 5; origTv.tv_sec = 1; // Shorter timeout for more responsive shutdown
origTv.tv_usec = 0; origTv.tv_usec = 0;
int _maxSd = _socket; int _maxSd = _socket;
std::set<int> activeSockets; std::set<int> activeSockets;
@@ -90,7 +112,8 @@ namespace Yc {
} }
} }
if (select(_maxSd + 1, &fd, NULL, NULL, &tv) > 0) { int selectResult = select(_maxSd + 1, &fd, NULL, NULL, &tv);
if (selectResult > 0) {
// Neue Verbindung? // Neue Verbindung?
if (FD_ISSET(_socket, &fd)) { if (FD_ISSET(_socket, &fd)) {
std::thread(&Server::handleRequest, this).detach(); std::thread(&Server::handleRequest, this).detach();
@@ -110,6 +133,13 @@ namespace Yc {
} }
} }
} }
} else if (selectResult < 0) {
if (errno == EINTR) {
// Interrupted by signal, check if we should stop
continue;
}
std::cerr << "[YourChat] select() error: " << strerror(errno) << std::endl;
break;
} }
// Aufgeräumte Sockets entfernen // Aufgeräumte Sockets entfernen
@@ -125,6 +155,7 @@ namespace Yc {
} }
} }
} }
std::cout << "[YourChat] Server run loop exiting" << std::endl;
} }
std::vector<std::string> Server::roomList() { std::vector<std::string> Server::roomList() {

View File

@@ -15,7 +15,9 @@ namespace Yc {
{ {
public: public:
Server(std::shared_ptr<Yc::Lib::Config> config, std::shared_ptr<Yc::Lib::Database> database); Server(std::shared_ptr<Yc::Lib::Config> config, std::shared_ptr<Yc::Lib::Database> database);
~Server();
void run(); void run();
void stop();
std::vector<std::string> roomList(); std::vector<std::string> roomList();
Json::Value jsonRoomList(); Json::Value jsonRoomList();
bool roomAllowed(std::string roomName, std::string userName, std::string password); bool roomAllowed(std::string roomName, std::string userName, std::string password);

View File

@@ -52,14 +52,35 @@ void SSLServer::run() {
} }
void SSLServer::stop() { void SSLServer::stop() {
std::cout << "[YourChat] Stopping SSL Server..." << std::endl;
_running = false; _running = false;
if (_context) lws_cancel_service(_context);
if (_serverThread.joinable()) _serverThread.join(); // Wake up the message queue thread
if (_messageThread.joinable()) _messageThread.join(); _queueCV.notify_all();
// Cancel libwebsockets service
if (_context) {
lws_cancel_service(_context);
}
// Wait for threads to finish
if (_serverThread.joinable()) {
std::cout << "[YourChat] Waiting for server thread to finish..." << std::endl;
_serverThread.join();
}
if (_messageThread.joinable()) {
std::cout << "[YourChat] Waiting for message thread to finish..." << std::endl;
_messageThread.join();
}
// Clean up libwebsockets context
if (_context) { if (_context) {
lws_context_destroy(_context); lws_context_destroy(_context);
_context = nullptr; _context = nullptr;
} }
std::cout << "[YourChat] SSL Server stopped." << std::endl;
} }
void SSLServer::startServer() { void SSLServer::startServer() {
@@ -88,8 +109,15 @@ void SSLServer::startServer() {
} }
while (_running) { while (_running) {
lws_service(_context, 50); // Use a shorter timeout to be more responsive to shutdown
int ret = lws_service(_context, 10);
if (ret < 0) {
std::cerr << "[YourChat] lws_service returned error: " << ret << std::endl;
break;
} }
}
std::cout << "[YourChat] SSL Server thread exiting" << std::endl;
} }
void SSLServer::processMessageQueue() { void SSLServer::processMessageQueue() {
@@ -97,7 +125,7 @@ void SSLServer::processMessageQueue() {
std::unique_lock<std::mutex> lock(_queueMutex); std::unique_lock<std::mutex> lock(_queueMutex);
_queueCV.wait(lock, [this](){ return !_messageQueue.empty() || !_running; }); _queueCV.wait(lock, [this](){ return !_messageQueue.empty() || !_running; });
while (!_messageQueue.empty()) { while (!_messageQueue.empty() && _running) {
std::string msg = std::move(_messageQueue.front()); std::string msg = std::move(_messageQueue.front());
_messageQueue.pop(); _messageQueue.pop();
lock.unlock(); lock.unlock();
@@ -106,6 +134,7 @@ void SSLServer::processMessageQueue() {
lock.lock(); lock.lock();
} }
} }
std::cout << "[YourChat] Message queue thread exiting" << std::endl;
} }
int SSLServer::wsCallback(struct lws *wsi, enum lws_callback_reasons reason, void *user, void *in, size_t len) { int SSLServer::wsCallback(struct lws *wsi, enum lws_callback_reasons reason, void *user, void *in, size_t len) {

View File

@@ -4,25 +4,64 @@
#include "core/ssl_server.h" #include "core/ssl_server.h"
#include "lib/database.h" #include "lib/database.h"
#include <iostream> #include <iostream>
#include <csignal>
#include <atomic>
#include <thread>
#include <chrono>
// Global flag for graceful shutdown
std::atomic<bool> g_running{true};
std::shared_ptr<Yc::Lib::SSLServer> g_sslServer = nullptr;
std::shared_ptr<Yc::Lib::Server> g_server = nullptr;
// Signal handler for graceful shutdown
void signalHandler(int signal) {
std::cout << "\n[YourChat] Received signal " << signal << ", shutting down gracefully..." << std::endl;
g_running = false;
if (g_sslServer) {
g_sslServer->stop();
}
if (g_server) {
g_server->stop();
}
}
// main function // main function
int main(int, char **) { int main(int, char **) {
// Set up signal handlers
std::signal(SIGINT, signalHandler);
std::signal(SIGTERM, signalHandler);
auto config = std::make_shared<Yc::Lib::Config>(); auto config = std::make_shared<Yc::Lib::Config>();
auto database = std::make_shared<Yc::Lib::Database>(config); auto database = std::make_shared<Yc::Lib::Database>(config);
// Check if SSL is enabled // Check if SSL is enabled
bool sslEnabled = config->value("server", "ssl_enabled").asBool(); bool sslEnabled = config->value("server", "ssl_enabled").asBool();
try {
if (sslEnabled) { if (sslEnabled) {
std::cout << "[YourChat] Starting with SSL/TLS support" << std::endl; std::cout << "[YourChat] Starting with SSL/TLS support" << std::endl;
auto sslServer = std::make_shared<Yc::Lib::SSLServer>(config, database); g_sslServer = std::make_shared<Yc::Lib::SSLServer>(config, database);
sslServer->createRooms(); g_sslServer->createRooms();
sslServer->run(); g_sslServer->run();
} else { } else {
std::cout << "[YourChat] Starting without SSL/TLS support" << std::endl; std::cout << "[YourChat] Starting without SSL/TLS support" << std::endl;
auto server = std::make_shared<Yc::Lib::Server>(config, database); g_server = std::make_shared<Yc::Lib::Server>(config, database);
server->createRooms(config->group("rooms")); g_server->createRooms(config->group("rooms"));
server->run(); g_server->run();
}
// Main loop - wait for shutdown signal
while (g_running) {
std::this_thread::sleep_for(std::chrono::milliseconds(100));
}
std::cout << "[YourChat] Shutdown complete." << std::endl;
} catch (const std::exception& e) {
std::cerr << "[YourChat] Error: " << e.what() << std::endl;
return 1;
} }
return 0; return 0;