Verbessere Shutdown-Logik und Ressourcenbereinigung in Server- und ChatRoom-Klassen

- Implementiere eine ordnungsgemäße Bereinigung von Server- und ChatRoom-Ressourcen beim Herunterfahren.
- Aktualisiere die Destruktoren in `ChatRoom` und `Server`, um sicherzustellen, dass alle Benutzer-Threads gestoppt und aufgeräumt werden.
- Füge Überprüfungen hinzu, um doppelte Stop-Vorgänge zu vermeiden und die Stabilität zu erhöhen.
- Optimiere die Logging-Ausgaben für den Server-Stopp-Prozess zur besseren Nachverfolgbarkeit.
This commit is contained in:
Torsten Schulz (local)
2025-09-04 14:32:12 +02:00
parent 8d0d1bc187
commit 2c6901f989
5 changed files with 79 additions and 9 deletions

View File

@@ -37,7 +37,31 @@ namespace Yc
thread = std::make_unique<std::thread>(&ChatRoom::run, this); thread = std::make_unique<std::thread>(&ChatRoom::run, this);
} }
ChatRoom::~ChatRoom() = default; ChatRoom::~ChatRoom() {
setStop();
// Stop all users first
for (auto& user : _users) {
if (user) {
user->stop();
}
}
// Wait for all user threads to finish
for (auto& user : _users) {
if (user && user->thread.joinable()) {
user->thread.join();
}
}
// Clear users list
_users.clear();
// Now stop and join the room thread
if (thread && thread->joinable()) {
thread->join();
}
}
void ChatRoom::run() void ChatRoom::run()
{ {

View File

@@ -48,6 +48,9 @@ namespace Yc
std::string color() const; std::string color() const;
void setParent(std::shared_ptr<ChatRoom> parent); void setParent(std::shared_ptr<ChatRoom> parent);
public:
std::thread thread;
private: private:
std::shared_ptr<ChatRoom> _parent; std::shared_ptr<ChatRoom> _parent;
Yc::Object::User _user; Yc::Object::User _user;
@@ -56,7 +59,6 @@ namespace Yc
int _socket; int _socket;
std::string _token; std::string _token;
bool _stop; bool _stop;
std::thread thread;
void send(std::string out); void send(std::string out);
void send(Json::Value out); void send(Json::Value out);

View File

@@ -22,7 +22,8 @@ namespace Yc {
Server::Server(std::shared_ptr<Yc::Lib::Config> config, std::shared_ptr<Yc::Lib::Database> database) : Server::Server(std::shared_ptr<Yc::Lib::Config> config, std::shared_ptr<Yc::Lib::Database> database) :
_config(std::move(config)), _config(std::move(config)),
_database(std::move(database)), _database(std::move(database)),
_stop(false) { _stop(false),
_socket(-1) {
int port = 1235; int port = 1235;
try { try {
Json::Value p = _config->value("server", "port"); Json::Value p = _config->value("server", "port");
@@ -43,24 +44,39 @@ namespace Yc {
// Fallback to IPv4 // Fallback to IPv4
close(_socket); close(_socket);
_socket = socket(AF_INET, SOCK_STREAM, 0); _socket = socket(AF_INET, SOCK_STREAM, 0);
if (_socket < 0) {
std::cout << "socket create failed" << std::endl;
exit(-1);
}
setsockopt(_socket, SOL_SOCKET, SO_REUSEADDR, (char *)&opt, sizeof(opt)); setsockopt(_socket, SOL_SOCKET, SO_REUSEADDR, (char *)&opt, sizeof(opt));
flags = 1; setsockopt(_socket, IPPROTO_TCP, TCP_NODELAY, (void *)&flags, sizeof(flags)); flags = 1; setsockopt(_socket, IPPROTO_TCP, TCP_NODELAY, (void *)&flags, sizeof(flags));
ka = 1; setsockopt(_socket, SOL_SOCKET, SO_KEEPALIVE, &ka, sizeof(ka)); ka = 1; setsockopt(_socket, SOL_SOCKET, SO_KEEPALIVE, &ka, sizeof(ka));
struct sockaddr_in addr4{}; addr4.sin_family = AF_INET; addr4.sin_addr.s_addr = INADDR_ANY; addr4.sin_port = htons(port); struct sockaddr_in addr4{}; addr4.sin_family = AF_INET; addr4.sin_addr.s_addr = INADDR_ANY; addr4.sin_port = htons(port);
if (bind(_socket, (struct sockaddr *)&addr4, sizeof(addr4)) != 0) { if (bind(_socket, (struct sockaddr *)&addr4, sizeof(addr4)) != 0) {
std::cout << "bind not possible" << std::endl; exit(-1); std::cout << "bind not possible on IPv4" << std::endl;
close(_socket);
_socket = -1;
exit(-1);
} }
std::cout << "[YourChat] Server gestartet. Lausche auf Port " << port << " (IPv4)" << std::endl; std::cout << "[YourChat] Server gestartet. Lausche auf Port " << port << " (IPv4)" << std::endl;
} }
} else { } else {
// Fallback to IPv4 directly // Fallback to IPv4 directly
_socket = socket(AF_INET, SOCK_STREAM, 0); _socket = socket(AF_INET, SOCK_STREAM, 0);
if (_socket < 0) { std::cout << "socket create failed" << std::endl; exit(-1);} if (_socket < 0) {
std::cout << "socket create failed" << std::endl;
exit(-1);
}
setsockopt(_socket, SOL_SOCKET, SO_REUSEADDR, (char *)&opt, sizeof(opt)); setsockopt(_socket, SOL_SOCKET, SO_REUSEADDR, (char *)&opt, sizeof(opt));
int flags = 1; setsockopt(_socket, IPPROTO_TCP, TCP_NODELAY, (void *)&flags, sizeof(flags)); int flags = 1; setsockopt(_socket, IPPROTO_TCP, TCP_NODELAY, (void *)&flags, sizeof(flags));
int ka = 1; setsockopt(_socket, SOL_SOCKET, SO_KEEPALIVE, &ka, sizeof(ka)); int ka = 1; setsockopt(_socket, SOL_SOCKET, SO_KEEPALIVE, &ka, sizeof(ka));
struct sockaddr_in addr4{}; addr4.sin_family = AF_INET; addr4.sin_addr.s_addr = INADDR_ANY; addr4.sin_port = htons(port); struct sockaddr_in addr4{}; addr4.sin_family = AF_INET; addr4.sin_addr.s_addr = INADDR_ANY; addr4.sin_port = htons(port);
if (bind(_socket, (struct sockaddr *)&addr4, sizeof(addr4)) != 0) { std::cout << "bind not possible" << std::endl; exit(-1);} if (bind(_socket, (struct sockaddr *)&addr4, sizeof(addr4)) != 0) {
std::cout << "bind not possible on IPv4" << std::endl;
close(_socket);
_socket = -1;
exit(-1);
}
std::cout << "[YourChat] Server gestartet. Lausche auf Port " << port << " (IPv4)" << std::endl; std::cout << "[YourChat] Server gestartet. Lausche auf Port " << port << " (IPv4)" << std::endl;
} }
} }
@@ -74,21 +90,35 @@ namespace Yc {
} }
void Server::stop() { void Server::stop() {
if (_stop) {
return; // Already stopped
}
std::cout << "[YourChat] Stopping regular server..." << std::endl; std::cout << "[YourChat] Stopping regular server..." << std::endl;
_stop = true; _stop = true;
// Stop all rooms // Stop all rooms
for (auto& room : _rooms) { for (auto& room : _rooms) {
room->setStop(); if (room) {
room->setStop();
}
} }
// Clear rooms list to ensure proper cleanup order
_rooms.clear();
std::cout << "[YourChat] Regular server stopped." << std::endl; std::cout << "[YourChat] Regular server stopped." << std::endl;
} }
void Server::run() { void Server::run() {
if (_socket < 0) {
std::cout << "Invalid socket, cannot start server" << std::endl;
return;
}
if (listen(_socket, 5) < 0) { if (listen(_socket, 5) < 0) {
std::cout << "listen not possible" << std::endl; std::cout << "listen not possible: " << strerror(errno) << std::endl;
exit(-1); return;
} }
timeval origTv; timeval origTv;
origTv.tv_sec = 1; // Shorter timeout for more responsive shutdown origTv.tv_sec = 1; // Shorter timeout for more responsive shutdown

View File

@@ -52,6 +52,10 @@ void SSLServer::run() {
} }
void SSLServer::stop() { void SSLServer::stop() {
if (!_running) {
return; // Already stopped
}
std::cout << "[YourChat] Stopping SSL Server..." << std::endl; std::cout << "[YourChat] Stopping SSL Server..." << std::endl;
_running = false; _running = false;

View File

@@ -57,6 +57,16 @@ int main(int, char **) {
std::this_thread::sleep_for(std::chrono::milliseconds(100)); std::this_thread::sleep_for(std::chrono::milliseconds(100));
} }
// Clean up servers before exit
if (g_sslServer) {
g_sslServer->stop();
g_sslServer.reset();
}
if (g_server) {
g_server->stop();
g_server.reset();
}
std::cout << "[YourChat] Shutdown complete." << std::endl; std::cout << "[YourChat] Shutdown complete." << std::endl;
} catch (const std::exception& e) { } catch (const std::exception& e) {