Füge Unterstützung für die Verwaltung von WebSocket-Verbindungen im ChatRoom und SSLServer hinzu

- Implementiere die Methode `findUserByToken` in `ChatRoom`, um Benutzer anhand ihres Tokens zu finden.
- Ergänze die Logik zur Entfernung von WebSocket-Verbindungen im `SSLServer`, wenn ein Benutzer die Verbindung trennt.
- Füge die Methode `isWebSocket` in `ChatUser` hinzu, um zu überprüfen, ob ein Benutzer über eine WebSocket-Verbindung verfügt.
- Aktualisiere die Verarbeitung von Verbindungsabbrüchen, um sicherzustellen, dass sowohl die ChatRoom- als auch die SSLServer-Verbindungen korrekt verwaltet werden.
- Ergänze Debug-Ausgaben zur Nachverfolgbarkeit der WebSocket-Verbindungen und deren Status.
This commit is contained in:
Torsten Schulz (local)
2025-09-05 16:20:56 +02:00
parent fe81e19bef
commit 59c6e46c08
6 changed files with 103 additions and 12 deletions

View File

@@ -2,6 +2,7 @@
#include <algorithm> #include <algorithm>
#include "server.h" #include "server.h"
#include "ssl_server.h"
#include <unistd.h> #include <unistd.h>
#include <iostream> #include <iostream>
#include <time.h> #include <time.h>
@@ -375,6 +376,23 @@ namespace Yc
{ {
// Spezielle Nachricht für Verbindungsabbrüche // Spezielle Nachricht für Verbindungsabbrüche
addMessage(ChatUser::system, std::string("user_disconnected"), (*it)->name(), (*it)->color()); addMessage(ChatUser::system, std::string("user_disconnected"), (*it)->name(), (*it)->color());
// Entferne auch die SSL-Server-Verbindung falls vorhanden
if ((*it)->isWebSocket()) {
// Finde den Token für diese Verbindung
std::string token = (*it)->getToken();
if (!token.empty()) {
// Entferne die Verbindung aus dem SSL-Server
SSLServer* sslServer = SSLServer::getInstance();
if (sslServer) {
sslServer->removeConnection(token);
#ifdef YC_DEBUG
std::cout << "[Debug] Removed SSL connection for token: " << token << std::endl;
#endif
}
}
}
_users.erase(it); _users.erase(it);
break; break;
} }
@@ -519,6 +537,18 @@ namespace Yc
return nullptr; return nullptr;
} }
std::shared_ptr<ChatUser> ChatRoom::findUserByToken(std::string token)
{
for (auto &user : _users)
{
if (user->getToken() == token)
{
return user;
}
}
return nullptr;
}
void ChatRoom::_handleDice() void ChatRoom::_handleDice()
{ {
if (((_type & rounds) == rounds)) if (((_type & rounds) == rounds))

View File

@@ -62,6 +62,7 @@ namespace Yc
bool accessAllowed(std::string userName, std::string password); bool accessAllowed(std::string userName, std::string password);
bool userIsInRoom(std::string userName); bool userIsInRoom(std::string userName);
std::shared_ptr<ChatUser> findUserByName(std::string userName); std::shared_ptr<ChatUser> findUserByName(std::string userName);
std::shared_ptr<ChatUser> findUserByToken(std::string token);
void addUserWhenQueueEmpty(std::shared_ptr<ChatUser> user); void addUserWhenQueueEmpty(std::shared_ptr<ChatUser> user);
bool userToNewRoom(std::shared_ptr<ChatUser> user, std::string newRoom, std::string password); bool userToNewRoom(std::shared_ptr<ChatUser> user, std::string newRoom, std::string password);
unsigned int flags(); unsigned int flags();

View File

@@ -4,6 +4,7 @@
#include "server.h" #include "server.h"
#include "lib/tools.h" #include "lib/tools.h"
#include "lib/base.h" #include "lib/base.h"
#include <libwebsockets.h>
#include <json/json.h> #include <json/json.h>
#include <unistd.h> #include <unistd.h>
#include <sys/socket.h> #include <sys/socket.h>
@@ -385,6 +386,11 @@ namespace Yc
return (toValidate.get() == this); return (toValidate.get() == this);
} }
bool ChatUser::isWebSocket() const
{
return _wsi != nullptr;
}
void ChatUser::sendMsg(MsgType type, const char *message, std::string userName, std::string color) void ChatUser::sendMsg(MsgType type, const char *message, std::string userName, std::string color)
{ {
sendMsg(type, std::string(message), userName, color); sendMsg(type, std::string(message), userName, color);
@@ -452,14 +458,20 @@ namespace Yc
if (heartbeatCounter >= HEARTBEAT_INTERVAL) { if (heartbeatCounter >= HEARTBEAT_INTERVAL) {
heartbeatCounter = 0; heartbeatCounter = 0;
// Prüfe Verbindung mit MSG_PEEK (nicht-blockierend) // Prüfe Verbindung mit MSG_PEEK (nicht-blockierend)
// Für WebSocket: Skip heartbeat check da libwebsockets das verwaltet if (_wsi) {
if (_wsi) { // WebSocket-Verbindung prüfen
#ifdef YC_DEBUG #ifdef YC_DEBUG
std::cout << "[Debug] WebSocket user, skipping socket check" << std::endl; std::cout << "[Debug] WebSocket user, checking connection status" << std::endl;
#endif #endif
continue;
} // Für WebSocket-Verbindungen verwenden wir einen einfacheren Ansatz:
// Wir senden einen Ping und prüfen, ob die Verbindung noch aktiv ist
// Da libwebsockets die Verbindung verwaltet, können wir nicht direkt prüfen
// Stattdessen verlassen wir uns auf die libwebsockets-Callbacks
// für die Erkennung von Verbindungsabbrüchen
continue;
}
char peek; char peek;
ssize_t r = recv(_socket, &peek, 1, MSG_PEEK | MSG_DONTWAIT); ssize_t r = recv(_socket, &peek, 1, MSG_PEEK | MSG_DONTWAIT);

View File

@@ -43,6 +43,7 @@ namespace Yc
std::string getToken() const; std::string getToken() const;
bool validateToken(std::string token); bool validateToken(std::string token);
bool isUser(std::shared_ptr<ChatUser> toValidate); bool isUser(std::shared_ptr<ChatUser> toValidate);
bool isWebSocket() const;
void sendMsg(MsgType type, std::string message, std::string userName, std::string color); void sendMsg(MsgType type, std::string message, std::string userName, std::string color);
void sendMsg(MsgType type, const char *message, std::string userName, std::string color); void sendMsg(MsgType type, const char *message, std::string userName, std::string color);
void sendMsg(MsgType type, Json::Value message, std::string userName, std::string color); void sendMsg(MsgType type, Json::Value message, std::string userName, std::string color);

View File

@@ -196,6 +196,15 @@ int SSLServer::wsCallback(struct lws *wsi, enum lws_callback_reasons reason, voi
break; break;
} }
case LWS_CALLBACK_CLOSED:
std::cout << "[YourChat] WebSocket-Verbindung geschlossen" << std::endl;
#ifdef YC_DEBUG
std::cout << "[Debug] WebSocket connection closed" << std::endl;
#endif
// Finde den User für diese Verbindung und entferne ihn
_instance->handleWebSocketDisconnect(wsi);
break;
case LWS_CALLBACK_SERVER_WRITEABLE: { case LWS_CALLBACK_SERVER_WRITEABLE: {
// Send ping to keep connection alive // Send ping to keep connection alive
unsigned char buf[LWS_PRE + 4]; unsigned char buf[LWS_PRE + 4];
@@ -204,9 +213,6 @@ int SSLServer::wsCallback(struct lws *wsi, enum lws_callback_reasons reason, voi
break; break;
} }
case LWS_CALLBACK_CLOSED:
// Connection closed - no need to access user data
break;
default: default:
break; break;
@@ -714,6 +720,39 @@ void SSLServer::addConnection(const std::string& token, struct lws *wsi) {
std::cout << "[YourChat] Verbindung für Token " << token << " gespeichert" << std::endl; std::cout << "[YourChat] Verbindung für Token " << token << " gespeichert" << std::endl;
} }
void SSLServer::handleWebSocketDisconnect(struct lws *wsi) {
// Finde den User für diese WebSocket-Verbindung
std::shared_lock<std::shared_mutex> lock(_connectionsMutex);
for (auto it = _connections.begin(); it != _connections.end(); ++it) {
if (it->second == wsi) {
std::string token = it->first;
lock.unlock(); // Unlock before calling removeConnection
#ifdef YC_DEBUG
std::cout << "[Debug] WebSocket disconnect detected for token: " << token << std::endl;
#endif
// Finde den User in den Räumen und entferne ihn
for (auto &room: _rooms) {
if (room) {
auto user = room->findUserByToken(token);
if (user) {
#ifdef YC_DEBUG
std::cout << "[Debug] Removing disconnected user: " << user->name() << std::endl;
#endif
room->removeUserDisconnected(user);
break;
}
}
}
// Entferne die Verbindung
removeConnection(token);
return;
}
}
}
void SSLServer::removeConnection(const std::string& token) { void SSLServer::removeConnection(const std::string& token) {
std::unique_lock<std::shared_mutex> lock(_connectionsMutex); std::unique_lock<std::shared_mutex> lock(_connectionsMutex);
_connections.erase(token); _connections.erase(token);
@@ -721,6 +760,10 @@ void SSLServer::removeConnection(const std::string& token) {
std::cout << "[YourChat] Verbindung für Token " << token << " entfernt" << std::endl; std::cout << "[YourChat] Verbindung für Token " << token << " entfernt" << std::endl;
} }
SSLServer* SSLServer::getInstance() {
return _instance;
}
std::shared_ptr<ChatUser> SSLServer::getUserByToken(const std::string& token) { std::shared_ptr<ChatUser> SSLServer::getUserByToken(const std::string& token) {
std::shared_lock<std::shared_mutex> lock(_connectionsMutex); std::shared_lock<std::shared_mutex> lock(_connectionsMutex);
auto it = _users.find(token); auto it = _users.find(token);

View File

@@ -58,12 +58,16 @@ public:
// WebSocket callbacks // WebSocket callbacks
static int wsCallback(struct lws *wsi, enum lws_callback_reasons reason, void *user, void *in, size_t len); static int wsCallback(struct lws *wsi, enum lws_callback_reasons reason, void *user, void *in, size_t len);
// Connection management
void removeConnection(const std::string& token);
static SSLServer* getInstance();
private: private:
void startServer(); void startServer();
void processMessageQueue(); void processMessageQueue();
void handleWebSocketMessage(struct lws *wsi, const std::string& message); void handleWebSocketMessage(struct lws *wsi, const std::string& message);
void handleWebSocketDisconnect(struct lws *wsi);
void addConnection(const std::string& token, struct lws *wsi); void addConnection(const std::string& token, struct lws *wsi);
void removeConnection(const std::string& token);
std::shared_ptr<ChatUser> getUserByToken(const std::string& token); std::shared_ptr<ChatUser> getUserByToken(const std::string& token);
std::shared_ptr<Config> _config; std::shared_ptr<Config> _config;