#include "server.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include namespace Yc { namespace Lib { Server::Server(std::shared_ptr config, std::shared_ptr database) : _config(std::move(config)), _database(std::move(database)), _stop(false), _socket(-1) { #ifdef YC_DEBUG std::cout << "[Debug] Server constructor called" << std::endl; #endif int port = 1235; try { Json::Value p = _config->value("server", "port"); if (p.isInt()) port = p.asInt(); } catch (...) {} #ifdef YC_DEBUG std::cout << "[Debug] Server port: " << port << std::endl; #endif int opt = 1; // Try IPv6 dual-stack (accepts IPv4 via v4-mapped if v6only=0) _socket = socket(AF_INET6, SOCK_STREAM, 0); if (_socket >= 0) { setsockopt(_socket, SOL_SOCKET, SO_REUSEADDR, (char *)&opt, sizeof(opt)); int v6only = 0; setsockopt(_socket, IPPROTO_IPV6, IPV6_V6ONLY, &v6only, sizeof(v6only)); 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)); struct sockaddr_in6 addr6{}; addr6.sin6_family = AF_INET6; addr6.sin6_addr = in6addr_any; addr6.sin6_port = htons(port); if (bind(_socket, (struct sockaddr *)&addr6, sizeof(addr6)) == 0) { std::cout << "[YourChat] Server gestartet. Lausche auf Port " << port << " (IPv6 dual-stack)" << std::endl; } else { // Fallback to IPv4 close(_socket); _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)); flags = 1; setsockopt(_socket, IPPROTO_TCP, TCP_NODELAY, (void *)&flags, sizeof(flags)); 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); 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; } } else { // Fallback to IPv4 directly _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)); 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)); 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 on IPv4" << std::endl; close(_socket); _socket = -1; exit(-1); } std::cout << "[YourChat] Server gestartet. Lausche auf Port " << port << " (IPv4)" << std::endl; } } Server::~Server() { stop(); if (_socket >= 0) { close(_socket); _socket = -1; } } void Server::stop() { if (_stop) { return; // Already stopped } std::cout << "[YourChat] Stopping regular server..." << std::endl; _stop = true; // Stop all rooms for (auto& room : _rooms) { if (room) { room->setStop(); } } // Clear rooms list to ensure proper cleanup order _rooms.clear(); std::cout << "[YourChat] Regular server stopped." << std::endl; } void Server::run() { #ifdef YC_DEBUG std::cout << "[Debug] Server::run() called" << std::endl; #endif if (_socket < 0) { std::cout << "Invalid socket, cannot start server" << std::endl; return; } if (listen(_socket, 5) < 0) { std::cout << "listen not possible: " << strerror(errno) << std::endl; return; } #ifdef YC_DEBUG std::cout << "[Debug] Server listening on socket: " << _socket << std::endl; #endif timeval origTv; origTv.tv_sec = 1; // Shorter timeout for more responsive shutdown origTv.tv_usec = 0; int _maxSd = _socket; std::set activeSockets; std::mutex socketMutex; while (!_stop) { timeval tv(origTv); fd_set fd; FD_ZERO(&fd); FD_SET(_socket, &fd); // Alle aktiven Client-Sockets hinzufügen { std::lock_guard lock(socketMutex); for (int clientSock : activeSockets) { FD_SET(clientSock, &fd); if (clientSock > _maxSd) _maxSd = clientSock; } } int selectResult = select(_maxSd + 1, &fd, NULL, NULL, &tv); if (selectResult > 0) { // Neue Verbindung? if (FD_ISSET(_socket, &fd)) { std::thread(&Server::handleRequest, this).detach(); } // Client-Socket-Aktivität? { std::lock_guard lock(socketMutex); auto it = activeSockets.begin(); while (it != activeSockets.end()) { if (FD_ISSET(*it, &fd)) { // Socket ist aktiv, aber handleRequest läuft bereits in separatem Thread // Hier könnten wir Heartbeat/Keepalive prüfen ++it; } else { ++it; } } } } 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 { std::lock_guard lock(socketMutex); auto it = activeSockets.begin(); while (it != activeSockets.end()) { if (activeSockets.count(*it) == 0) { it = activeSockets.erase(it); } else { ++it; } } } } std::cout << "[YourChat] Server run loop exiting" << std::endl; } std::vector Server::roomList() { std::vector list; for (const auto &room: _rooms) { list.push_back(room->name()); } return list; } Json::Value Server::jsonRoomList() { Json::Value list; for (auto &room: _rooms) { Json::Value roomJson = Json::objectValue; roomJson["name"] = room->name(); roomJson["flags"] = room->flags(); list.append(roomJson); } return list; } 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) { 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; } bool Server::changeRoom(std::shared_ptr user, std::string newRoom, std::string password) { 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->getToken(), true); // silent = true, da wir eigene Nachrichten senden break; } } // Füge User zum neuen Raum hinzu for (auto &room: _rooms) { if (room->name() == newRoom) { #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::reloadRooms() { std::cout << "[YourChat] Reloading rooms from database..." << std::endl; // Clear existing rooms _rooms.clear(); // Reload from database Json::Value emptyRoomList; createRooms(emptyRoomList); std::cout << "[YourChat] Reloaded " << _rooms.size() << " rooms from database" << std::endl; } 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(); room["name"] = row["title"].c_str(); room["password"] = row["password_hash"].is_null() ? "" : row["password_hash"].c_str(); room["type"] = row["room_type_id"].is_null() ? 0 : row["room_type_id"].as(); room["is_public"] = row["is_public"].as(); room["owner_id"] = row["owner_id"].is_null() ? 0 : row["owner_id"].as(); room["min_age"] = row["min_age"].is_null() ? 0 : row["min_age"].as(); room["max_age"] = row["max_age"].is_null() ? 0 : row["max_age"].as(); room["created_at"] = row["created_at"].c_str(); room["updated_at"] = row["updated_at"].c_str(); room["room_type"] = row["room_type"].is_null() ? "" : row["room_type"].c_str(); // Platzhalter für Felder, die im Konstruktor benötigt werden room["allowed"] = Json::arrayValue; // ggf. später befüllen room["roundlength"] = 60; // Default-Wert auto newRoom = std::make_shared(self, room); newRoom->setDatabase(_database); _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 (...) { #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(self, room); newRoom->setDatabase(_database); _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"; room["password"] = ""; room["allowed"] = Json::arrayValue; room["type"] = 0; room["roundlength"] = 0; auto newRoom = std::make_shared(self, room); newRoom->setDatabase(_database); _rooms.push_back(newRoom); 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() { #ifdef YC_DEBUG std::cout << "[Debug] handleRequest called" << std::endl; #endif struct sockaddr_in sockAddr; socklen_t sockAddrLen = sizeof(sockAddr); int userSock = accept(_socket, (struct sockaddr *)&sockAddr, &sockAddrLen); if (userSock < 0) { #ifdef YC_DEBUG std::cout << "[Debug] accept failed: " << strerror(errno) << std::endl; #endif return; } #ifdef YC_DEBUG std::cout << "[Debug] New connection accepted, socket: " << userSock << std::endl; #endif // Neuen Socket zur Überwachung hinzufügen { std::lock_guard lock(socketMutex); activeSockets.insert(userSock); } // Log jede akzeptierte Verbindung char clientIP[INET_ADDRSTRLEN]; inet_ntop(AF_INET, &(sockAddr.sin_addr), clientIP, INET_ADDRSTRLEN); 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)); // 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; #endif if (msg == "") { return; } // OPTIONS Request (CORS Preflight) if (msg.rfind("OPTIONS ", 0) == 0) { std::ostringstream resp; resp << "HTTP/1.1 200 OK\r\n" << "Access-Control-Allow-Origin: *\r\n" << "Access-Control-Allow-Methods: GET, POST, OPTIONS\r\n" << "Access-Control-Allow-Headers: Upgrade, Connection, Sec-WebSocket-Key, Sec-WebSocket-Version, Sec-WebSocket-Protocol, Origin\r\n" << "Access-Control-Allow-Credentials: true\r\n" << "Access-Control-Max-Age: 86400\r\n" << "Content-Length: 0\r\n" << "\r\n"; ::send(userSock, resp.str().c_str(), resp.str().size(), 0); close(userSock); return; } // WebSocket Upgrade? if (msg.rfind("GET ", 0) == 0 && msg.find("Upgrade: websocket") != std::string::npos) { // sehr einfacher Header-Parser std::string key; std::string subprotocol; std::string origin; std::string version; std::string extensions; std::istringstream iss(msg); std::string line; #ifdef YC_DEBUG std::cout << "[Debug] === WebSocket Headers ===" << std::endl; #endif while (std::getline(iss, line)) { if (!line.empty() && (line.back() == '\r' || line.back() == '\n')) line.pop_back(); auto pos = line.find(":"); if (pos != std::string::npos) { std::string h = line.substr(0, pos); std::string v = line.substr(pos+1); // trim auto ltrim = [](std::string &s){ s.erase(s.begin(), std::find_if(s.begin(), s.end(), [](int ch){return !std::isspace(ch);}));}; auto rtrim = [](std::string &s){ s.erase(std::find_if(s.rbegin(), s.rend(), [](int ch){return !std::isspace(ch);}).base(), s.end());}; ltrim(h); rtrim(h); ltrim(v); rtrim(v); #ifdef YC_DEBUG std::cout << "[Debug] Header: '" << h << "' = '" << v << "'" << std::endl; #endif if (strcasecmp(h.c_str(), "Sec-WebSocket-Key") == 0) { key = v; } else if (strcasecmp(h.c_str(), "Sec-WebSocket-Protocol") == 0) { subprotocol = v; } else if (strcasecmp(h.c_str(), "Origin") == 0) { origin = v; } else if (strcasecmp(h.c_str(), "Sec-WebSocket-Version") == 0) { version = v; } else if (strcasecmp(h.c_str(), "Sec-WebSocket-Extensions") == 0) { extensions = v; } } if (line.empty()) break; } #ifdef YC_DEBUG std::cout << "[Debug] === Parsed Values ===" << std::endl; std::cout << "[Debug] Key: " << (key.empty() ? "MISSING" : key) << std::endl; std::cout << "[Debug] Protocol: " << (subprotocol.empty() ? "NONE" : subprotocol) << std::endl; std::cout << "[Debug] Origin: " << (origin.empty() ? "MISSING" : origin) << std::endl; std::cout << "[Debug] Version: " << (version.empty() ? "MISSING" : version) << std::endl; std::cout << "[Debug] Extensions: " << (extensions.empty() ? "NONE" : extensions) << std::endl; std::cout << "[Debug] ======================" << std::endl; #endif if (!key.empty()) { std::string accept = Base::webSocketAcceptKey(key); std::ostringstream resp; resp << "HTTP/1.1 101 Switching Protocols\r\n" << "Upgrade: websocket\r\n" << "Connection: Upgrade\r\n" << "Sec-WebSocket-Accept: " << accept << "\r\n"; // CORS-Header hinzufügen resp << "Access-Control-Allow-Origin: *\r\n" << "Access-Control-Allow-Methods: GET, POST, OPTIONS\r\n" << "Access-Control-Allow-Headers: Upgrade, Connection, Sec-WebSocket-Key, Sec-WebSocket-Version, Sec-WebSocket-Protocol, Origin\r\n" << "Access-Control-Allow-Credentials: true\r\n"; // Subprotokoll-Unterstützung if (!subprotocol.empty()) { resp << "Sec-WebSocket-Protocol: " << subprotocol << "\r\n"; } else { // Fallback: Standard-Subprotokoll anbieten resp << "Sec-WebSocket-Protocol: chat\r\n"; } resp << "\r\n"; #ifdef YC_DEBUG std::cout << "[Debug] === WebSocket Response ===" << std::endl; std::cout << "[Debug] " << resp.str() << std::endl; std::cout << "[Debug] =========================" << std::endl; #endif ::send(userSock, resp.str().c_str(), resp.str().size(), 0); Base::markWebSocket(userSock); // Sofort eine Willkommen-Nachricht senden, um Firefox's Timing-Problem zu lösen // Verwende korrektes WebSocket-Framing std::string welcomeMsg = "{\"type\":\"welcome\",\"message\":\"WebSocket-Verbindung hergestellt\"}"; Base::sendWebSocketMessage(userSock, welcomeMsg); #ifdef YC_DEBUG std::cout << "[Debug] WS upgrade ok, welcome message sent, waiting for init... fd=" << userSock << std::endl; #endif // Jetzt WebSocket Nachrichten lesen und an inputSwitcher weitergeben bool ownedByUser = false; while (true) { std::string wmsg = readSocket(userSock); if (wmsg.empty()) break; ownedByUser = inputSwitcher(userSock, wmsg); if (ownedByUser) break; } if (ownedByUser) { #ifdef YC_DEBUG std::cout << "[Debug] Ownership transferred to ChatUser, stop reading in Server for fd=" << userSock << std::endl; #endif } if (!ownedByUser) { #ifdef YC_DEBUG std::cout << "[Debug] WS closing (no ownership) fd=" << userSock << std::endl; #endif Base::unmarkWebSocket(userSock); close(userSock); } return; } } // Fallback: Plain JSON bool owned = inputSwitcher(userSock, msg); if (!owned) { #ifdef YC_DEBUG std::cout << "[Debug] Plain JSON path without ownership, closing fd=" << userSock << std::endl; #endif close(userSock); } } bool Server::inputSwitcher(int userSocket, std::string input) { Json::Value inputTree = getJsonTree(input); #ifdef YC_DEBUG std::cout << "[Debug] inputSwitcher: type=" << inputTree["type"].asString() << std::endl; #endif if (inputTree["type"] == "init") { initUser(userSocket, inputTree); return true; // ChatUser übernimmt nun den Socket } else { } return false; } bool Server::userExists(std::string userName) { for (const auto &room: _rooms) { if (room->userNameExists(userName)) { return true; } } return false; } void Server::initUser(int userSocket, Json::Value data) { std::string name = data.isMember("name") ? data["name"].asString() : ""; std::string room = data.isMember("room") ? data["room"].asString() : ""; std::string color = data.isMember("color") ? data["color"].asString() : "#000000"; std::string password = data.isMember("password") ? data["password"].asString() : ""; #ifdef YC_DEBUG std::cout << "[Debug] initUser: name=" << name << ", room=" << room << ", color=" << color << std::endl; #endif if (name.empty() || room.empty()) { Json::Value errorJson; errorJson["type"] = ChatUser::error; errorJson["message"] = "missing_fields"; errorJson["detail"] = "'name' und 'room' müssen gesetzt sein."; send(userSocket, errorJson); close(userSocket); return; } if (userExists(name)) { Json::Value errorJson; errorJson["type"] = ChatUser::error; errorJson["message"] = "loggedin"; send(userSocket, errorJson); close(userSocket); return; } bool added(false); for (auto &roomObj: _rooms) { if (roomObj->name() == room) { if (roomObj->addUser(name, color, password, userSocket)) { added = true; break; } } } if (!added) { Json::Value errorJson; errorJson["type"] = ChatUser::error; errorJson["message"] = "room_not_found_or_join_failed"; send(userSocket, errorJson); close(userSocket); } } } // namespace Lib } // namespace Yp