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:
@@ -147,11 +147,29 @@ namespace Yc {
|
||||
}
|
||||
|
||||
bool Server::roomAllowed(std::string roomName, std::string userName, std::string password){
|
||||
#ifdef YC_DEBUG
|
||||
std::cout << "[Debug] roomAllowed called with roomName: '" << roomName << "', userName: '" << userName << "'" << std::endl;
|
||||
std::cout << "[Debug] Available rooms: ";
|
||||
for (auto &room: _rooms) {
|
||||
if (room->name() == roomName && room->accessAllowed(userName, password)) {
|
||||
std::cout << "'" << room->name() << "' ";
|
||||
}
|
||||
std::cout << std::endl;
|
||||
#endif
|
||||
|
||||
for (auto &room: _rooms) {
|
||||
#ifdef YC_DEBUG
|
||||
std::cout << "[Debug] Checking room: '" << room->name() << "' against requested: '" << roomName << "'" << std::endl;
|
||||
#endif
|
||||
if (room->name() == roomName && room->accessAllowed(userName, password)) {
|
||||
#ifdef YC_DEBUG
|
||||
std::cout << "[Debug] Room found and access allowed!" << std::endl;
|
||||
#endif
|
||||
return true;
|
||||
}
|
||||
}
|
||||
#ifdef YC_DEBUG
|
||||
std::cout << "[Debug] Room not found or access denied" << std::endl;
|
||||
#endif
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -159,42 +177,97 @@ namespace Yc {
|
||||
if (!roomAllowed(newRoom, user->name(), password)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
std::string oldRoomName = "";
|
||||
// Finde den aktuellen Raum des Users
|
||||
for (auto &room: _rooms) {
|
||||
if (room->userIsInRoom(user->name())) {
|
||||
oldRoomName = room->name();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
Json::Value userMsg = Json::objectValue;
|
||||
userMsg["tr"] = "room_change_user";
|
||||
userMsg["to"] = newRoom;
|
||||
|
||||
// Nur Nachrichten senden, wenn der User bereits in einem Raum ist (Raumwechsel)
|
||||
if (!oldRoomName.empty()) {
|
||||
for (auto &room: _rooms) {
|
||||
if (room->name() == oldRoomName) {
|
||||
// Sende Nachricht an alle User im alten Raum, dass der User den Raum verlassen hat
|
||||
Json::Value leaveMsg = Json::objectValue;
|
||||
leaveMsg["tr"] = "user_left_room";
|
||||
leaveMsg["userName"] = user->name();
|
||||
leaveMsg["userColor"] = user->color();
|
||||
leaveMsg["destination"] = newRoom;
|
||||
room->addMessage(ChatUser::system, leaveMsg, "", "");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Entferne User aus dem alten Raum
|
||||
for (auto &room: _rooms) {
|
||||
if (room->userIsInRoom(user->name())) {
|
||||
room->removeUser(user);
|
||||
Json::Value msg = Json::objectValue;
|
||||
msg["tr"] = "room_change_to";
|
||||
msg["to"] = newRoom;
|
||||
userMsg["from"] = room->name();
|
||||
room->addMessage(ChatUser::system, msg, user->name(), user->color());
|
||||
room->removeUser(user->getToken(), true); // silent = true, da wir eigene Nachrichten senden
|
||||
break;
|
||||
}
|
||||
}
|
||||
user->sendMsg(ChatUser::system, userMsg, "", "");
|
||||
|
||||
// Füge User zum neuen Raum hinzu
|
||||
for (auto &room: _rooms) {
|
||||
if (room->name() == newRoom) {
|
||||
Json::Value msg = Json::objectValue;
|
||||
msg["tr"] = "room_change_to";
|
||||
msg["from"] = userMsg["from"];
|
||||
room->addMessage(ChatUser::system, msg, user->name(), user->color());
|
||||
#ifdef YC_DEBUG
|
||||
std::cout << "[Debug] changeRoom: Adding user '" << user->name() << "' to room '" << newRoom << "'" << std::endl;
|
||||
std::cout << "[Debug] changeRoom: oldRoomName = '" << (oldRoomName.empty() ? "EMPTY" : oldRoomName) << "'" << std::endl;
|
||||
#endif
|
||||
|
||||
room->addUserWhenQueueEmpty(user);
|
||||
|
||||
// Nur bei Raumwechsel (nicht beim ersten Beitritt) die Nachricht senden
|
||||
if (!oldRoomName.empty()) {
|
||||
#ifdef YC_DEBUG
|
||||
std::cout << "[Debug] changeRoom: Sending 'user_entered_room' message (room change)" << std::endl;
|
||||
#endif
|
||||
Json::Value joinMsg = Json::objectValue;
|
||||
joinMsg["tr"] = "user_entered_room";
|
||||
joinMsg["userName"] = user->name();
|
||||
joinMsg["userColor"] = user->color();
|
||||
joinMsg["origin"] = oldRoomName;
|
||||
room->addMessage(ChatUser::system, joinMsg, "", "");
|
||||
} else {
|
||||
#ifdef YC_DEBUG
|
||||
std::cout << "[Debug] changeRoom: NOT sending 'user_entered_room' message (first join)" << std::endl;
|
||||
#endif
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void Server::createRooms(Json::Value roomList) {
|
||||
auto self = shared_from_this();
|
||||
bool created = false;
|
||||
|
||||
#ifdef YC_DEBUG
|
||||
std::cout << "[Debug] createRooms called with roomList size: " << roomList.size() << std::endl;
|
||||
std::cout << "[Debug] roomList content: " << roomList << std::endl;
|
||||
#endif
|
||||
|
||||
try {
|
||||
#ifdef YC_DEBUG
|
||||
std::cout << "[Debug] Attempting to load rooms from database..." << std::endl;
|
||||
#endif
|
||||
std::string query = R"(
|
||||
SELECT r.id, r.title, r.password_hash, r.room_type_id, r.is_public, r.owner_id, r.min_age, r.max_age, r.created_at, r.updated_at, rt.tr as room_type
|
||||
FROM chat.room r
|
||||
LEFT JOIN chat.room_type rt ON r.room_type_id = rt.id
|
||||
)";
|
||||
auto result = _database->exec(query);
|
||||
#ifdef YC_DEBUG
|
||||
std::cout << "[Debug] Database query result size: " << result.size() << std::endl;
|
||||
#endif
|
||||
for (const auto& row : result) {
|
||||
Json::Value room;
|
||||
room["id"] = row["id"].as<int>();
|
||||
@@ -214,19 +287,41 @@ namespace Yc {
|
||||
auto newRoom = std::make_shared<ChatRoom>(self, room);
|
||||
_rooms.push_back(newRoom);
|
||||
created = true;
|
||||
#ifdef YC_DEBUG
|
||||
std::cout << "[Debug] Created room from database: " << room["name"].asString() << std::endl;
|
||||
#endif
|
||||
}
|
||||
} catch (const std::exception& e) {
|
||||
#ifdef YC_DEBUG
|
||||
std::cout << "[Debug] Database error: " << e.what() << std::endl;
|
||||
#endif
|
||||
} catch (...) {
|
||||
// ignore DB errors, fallback below
|
||||
#ifdef YC_DEBUG
|
||||
std::cout << "[Debug] Unknown database error occurred" << std::endl;
|
||||
#endif
|
||||
}
|
||||
|
||||
if (!created) {
|
||||
#ifdef YC_DEBUG
|
||||
std::cout << "[Debug] Database loading failed, trying fallback rooms..." << std::endl;
|
||||
#endif
|
||||
// fallback to provided JSON room list (if any)
|
||||
if (roomList.isArray() && roomList.size() > 0) {
|
||||
#ifdef YC_DEBUG
|
||||
std::cout << "[Debug] Loading " << roomList.size() << " fallback rooms from config" << std::endl;
|
||||
#endif
|
||||
for (const auto& room : roomList) {
|
||||
#ifdef YC_DEBUG
|
||||
std::cout << "[Debug] Creating fallback room: " << room["name"].asString() << std::endl;
|
||||
#endif
|
||||
auto newRoom = std::make_shared<ChatRoom>(self, room);
|
||||
_rooms.push_back(newRoom);
|
||||
created = true;
|
||||
}
|
||||
} else {
|
||||
#ifdef YC_DEBUG
|
||||
std::cout << "[Debug] No fallback rooms in config, creating default room" << std::endl;
|
||||
#endif
|
||||
// final fallback: builtin default room
|
||||
Json::Value room;
|
||||
room["name"] = "Halle";
|
||||
@@ -239,6 +334,13 @@ namespace Yc {
|
||||
created = true;
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef YC_DEBUG
|
||||
std::cout << "[Debug] Total rooms created: " << _rooms.size() << std::endl;
|
||||
for (const auto& room : _rooms) {
|
||||
std::cout << "[Debug] Room: " << room->name() << std::endl;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
void Server::handleRequest() {
|
||||
@@ -260,10 +362,36 @@ namespace Yc {
|
||||
std::cout << "[YourChat] Verbindung akzeptiert von " << clientIP << ":" << ntohs(sockAddr.sin_port) << " (fd=" << userSock << ")" << std::endl;
|
||||
int flags = 1;
|
||||
setsockopt(userSock, IPPROTO_TCP, TCP_NODELAY, (void *)&flags, sizeof(flags));
|
||||
int ka2 = 1; setsockopt(userSock, SOL_SOCKET, SO_KEEPALIVE, &ka2, sizeof(ka2));
|
||||
|
||||
// Aggressive TCP Keep-Alive für schnelle Verbindungsabbruch-Erkennung
|
||||
int ka2 = 1;
|
||||
setsockopt(userSock, SOL_SOCKET, SO_KEEPALIVE, &ka2, sizeof(ka2));
|
||||
|
||||
// Keep-Alive Parameter: 5 Sekunden bis zum ersten Probe, dann alle 2 Sekunden
|
||||
int keepalive_time = 5; // 5 Sekunden bis zum ersten Probe
|
||||
int keepalive_intvl = 2; // 2 Sekunden zwischen Probes
|
||||
int keepalive_probes = 3; // 3 Probes bevor Verbindung als tot betrachtet wird
|
||||
|
||||
setsockopt(userSock, IPPROTO_TCP, TCP_KEEPIDLE, &keepalive_time, sizeof(keepalive_time));
|
||||
setsockopt(userSock, IPPROTO_TCP, TCP_KEEPINTVL, &keepalive_intvl, sizeof(keepalive_intvl));
|
||||
setsockopt(userSock, IPPROTO_TCP, TCP_KEEPCNT, &keepalive_probes, sizeof(keepalive_probes));
|
||||
|
||||
// Begrenze Blockierzeit beim Senden, um langsame Clients nicht alle zu verzögern
|
||||
timeval sendTimeout; sendTimeout.tv_sec = 0; sendTimeout.tv_usec = 500000; // 500ms
|
||||
setsockopt(userSock, SOL_SOCKET, SO_SNDTIMEO, &sendTimeout, sizeof(sendTimeout));
|
||||
|
||||
// Socket-Optionen für schnellere Fehlererkennung
|
||||
int reuseAddr = 1;
|
||||
setsockopt(userSock, SOL_SOCKET, SO_REUSEADDR, &reuseAddr, sizeof(reuseAddr));
|
||||
|
||||
// LINGER-Option: Sofort schließen bei Verbindungsabbruch
|
||||
struct linger linger_opt;
|
||||
linger_opt.l_onoff = 1;
|
||||
linger_opt.l_linger = 0; // 0 Sekunden = sofort schließen
|
||||
setsockopt(userSock, SOL_SOCKET, SO_LINGER, &linger_opt, sizeof(linger_opt));
|
||||
|
||||
// TCP_NODELAY bereits gesetzt (oben)
|
||||
// TCP Keep-Alive bereits konfiguriert (oben)
|
||||
std::string msg = readSocket(userSock);
|
||||
#ifdef YC_DEBUG
|
||||
std::cout << "[Debug] Neue Anfrage erhalten: " << msg << std::endl;
|
||||
|
||||
Reference in New Issue
Block a user