virtual void Install () override { auto & server=Server::Get(); // Get settings auto & data=server.Data(); timeout=data.GetSetting(timeout_setting,timeout_default); keep_alive=data.GetSetting(keep_alive_setting,keep_alive_default); // Install handler for incoming keep alives server.Router( recv::PacketID, recv::State )=[this] (PacketEvent event) mutable { handler(std::move(event)); }; // Queue up timed callback server.Pool().Enqueue( get_callback_freq(), [this] () mutable { callback(); } ); // Attach to connect/disconnect events // to add/remove data structures server.OnConnect.Add([this] (SmartPointer<Client> client) mutable { lock.Execute([&] () mutable { map.emplace(std::move(client),KeepAliveInfo()); }); }); server.OnDisconnect.Add([this] (SmartPointer<Client> client, const String &) mutable { lock.Execute([&] () mutable { map.erase(client); }); }); }
void Complete (Vector<DNSRecord> results) { lock.Execute([&] () mutable { // Fast fail if (failed) return; // Try and add each result from this set // to the aggregate set try { for (auto & result : results) this->results.Add(std::move(result)); // On exception, dutifully fail } catch (...) { promise.Fail(std::current_exception()); failed=true; } // If this is the last one in, complete // the promise if ((++completed)==num) promise.Complete(std::move(this->results)); }); }
void Fail (std::exception_ptr ex) noexcept { lock.Execute([&] () mutable { promise.Fail(std::move(ex)); failed=true; }); }
// Handles incoming packets void handler (PacketEvent event) { auto & packet=event.Data.Get<recv>(); // Check the ID, if it's zero then // it's a client-initiated keep alive // and we just reply in kind if (packet.KeepAliveID==0) { send reply; reply.KeepAliveID=0; event.From->Send(reply); // Otherwise we have to check to make // sure this is the right ID etc. } else { UInt64 elapsed; if (lock.Execute([&] () mutable { // Look up data about this // client auto iter=map.find(event.From); // If the client's data can't be // found, just stop processing if (iter==map.end()) return false; auto & data=iter->second; // Stop the timer at once elapsed=data.Latency.ElapsedMilliseconds(); // If we're not actually // waiting for a keep alive // from this client, or this // client sent the wrong ID, // we kill them with a protocol // error if (!( data.Waiting && (data.ID==packet.KeepAliveID) )) { event.From->Disconnect(protocol_error); return false; } // Everything is good, update // the client's latency event.From->Ping=elapsed; // We're no longer waiting data.Waiting=false; return true; })) { // Log if applicable auto & server=Server::Get(); if (server.IsVerbose(debug_key)) server.WriteLog( String::Format( log_ping, event.From->IP(), event.From->Port(), elapsed ), Service::LogType::Debug ); } } }
// Handles timed callbacks void callback () { auto & server=Server::Get(); try { // Cache whether or not we're doing // verbose logging bool is_verbose=server.IsVerbose(debug_key); // Loop for all clients for (auto & client : server.Clients) { // Determine how long this client has been // inactive auto inactive=client->Inactive(); // Did the client time out? bool timed_out=inactive>timeout; // Debug logging if applicable if (is_verbose) server.WriteLog( String::Format( log_inactive, client->IP(), client->Port(), inactive, timed_out ? log_action_terminate : log_action_keep ), Service::LogType::Debug ); // Kill if client has timed out if (timed_out) { client->Disconnect( String::Format( ::timed_out, timeout, inactive ) ); // Send them a ping, but only if they're // in the correct state } else if (client->GetState()==ProtocolState::Play) { // Generate a random ID that isn't zero // (since zero is reserved for the client's // use) auto id=dist(gen); if (id==0) ++id; lock.Execute([&] () mutable { // Look up client's data auto iter=map.find(client); // If the data could not be found, // just abort if (iter==map.end()) return; auto & data=iter->second; // Are we still waiting on a previous // ping? if (data.Waiting) { // Kill the client client->Disconnect( String::Format( ping_timed_out, data.Latency.ElapsedMilliseconds() ) ); return; } // Send a new keep alive send packet; packet.KeepAliveID=id; client->Send(packet); // We're now waiting data.Waiting=true; data.ID=id; }); } } // Queue up next iteration server.Pool().Enqueue( get_callback_freq(), [this] () mutable { callback(); } ); } catch (...) { // Panic try { server.Panic(std::current_exception()); } catch (...) { } throw; } }