Implementiere Benutzerverbindungskontrolle und verbessere Nachrichtenverwaltung

- Füge die Methode `removeUserDisconnected` in der ChatRoom-Klasse hinzu, um Benutzer bei Verbindungsabbrüchen zu entfernen und entsprechende Nachrichten zu senden.
- Aktualisiere die ChatUser-Klasse, um einen Token-Getter bereitzustellen und die Verbindungsprüfung zu optimieren.
- Ändere die Server-Klasse, um die Benutzerverwaltung bei Raumwechseln zu verbessern und Debug-Informationen hinzuzufügen.
- Optimiere die Socket-Optionen für eine schnellere Fehlererkennung und verbessere die Handhabung von Anfragen.
This commit is contained in:
Torsten Schulz (local)
2025-09-01 15:30:19 +02:00
parent 7338f1a740
commit d92c40748e
12 changed files with 868 additions and 33 deletions

View File

@@ -111,6 +111,11 @@ namespace Yc
return _name;
}
std::string ChatUser::getToken() const
{
return _token;
}
bool ChatUser::validateToken(std::string token)
{
return (token == _token);
@@ -155,16 +160,57 @@ namespace Yc
void ChatUser::checkerTask()
{
try {
// Heartbeat-Intervall: Alle 10 Sekunden Verbindung prüfen
const int HEARTBEAT_INTERVAL = 2;
int heartbeatCounter = 0;
while (!_stop)
{
fd_set readSd;
FD_ZERO(&readSd);
FD_SET(_socket, &readSd);
timeval tv;
tv.tv_sec = 0;
tv.tv_usec = 500;
int selectResult = select(_socket + 1, &readSd, NULL, NULL, &tv);
if (selectResult == 1 && FD_ISSET(_socket, &readSd) == 1)
std::this_thread::sleep_for(std::chrono::seconds(1));
// Heartbeat-Check alle 10 Sekunden
heartbeatCounter++;
if (heartbeatCounter >= HEARTBEAT_INTERVAL) {
heartbeatCounter = 0;
// Prüfe Verbindung mit MSG_PEEK (nicht-blockierend)
char peek;
ssize_t r = recv(_socket, &peek, 1, MSG_PEEK | MSG_DONTWAIT);
if (r == 0) {
#ifdef YC_DEBUG
std::cout << "[Debug] Verbindung zum Client abgebrochen (Token: <hidden>)" << std::endl;
#endif
_parent->removeUserDisconnected(shared_from_this());
_stop = true;
if (thread.joinable() && std::this_thread::get_id() == thread.get_id()) {
thread.detach();
}
return;
} else if (r < 0) {
// EINTR = Interrupted system call (normal), EAGAIN/EWOULDBLOCK = No data available (normal)
// Andere Fehler bedeuten Verbindungsabbruch
if (errno != EINTR && errno != EAGAIN && errno != EWOULDBLOCK) {
#ifdef YC_DEBUG
std::cout << "[Debug] Socket-Fehler: " << strerror(errno) << " (Token: <hidden>)" << std::endl;
#endif
_parent->removeUserDisconnected(shared_from_this());
_stop = true;
if (thread.joinable() && std::this_thread::get_id() == thread.get_id()) {
thread.detach();
}
return;
}
}
// Optional: Sende Heartbeat-Ping an Client
// (kann helfen, NAT/Firewall-Verbindungen aktiv zu halten)
#ifdef YC_DEBUG
std::cout << "[Debug] Heartbeat check passed for user: " << _name << std::endl;
#endif
}
// Ursprüngliche Verbindungsprüfung (alle 1 Sekunde)
{
char peek;
ssize_t r = recv(_socket, &peek, 1, MSG_PEEK);
@@ -172,7 +218,7 @@ namespace Yc
#ifdef YC_DEBUG
std::cout << "[Debug] Verbindung zum Client abgebrochen (Token: <hidden>)" << std::endl;
#endif
_parent->removeUser(_token);
_parent->removeUserDisconnected(shared_from_this());
_stop = true;
if (thread.joinable() && std::this_thread::get_id() == thread.get_id()) {
thread.detach();
@@ -180,6 +226,7 @@ namespace Yc
return;
}
}
std::string msg = readSocket(_socket);
if (msg == "")
{
@@ -347,7 +394,7 @@ namespace Yc
}
else if (jsonTree["type"].asString() == "join")
{
changeRoom(jsonTree["newroom"].asString(), jsonTree["password"].asString());
changeRoom(jsonTree["room"].asString(), jsonTree["password"].asString());
}
else if (jsonTree["type"].asString() == "userlist")
{