diff --git a/client.cpp b/client.cpp index 306bbaa..b743efe 100644 --- a/client.cpp +++ b/client.cpp @@ -17,6 +17,7 @@ Client::Client(int fd, ThreadData_p threadData) : Client::~Client() { + std::cout << "Removing client: " << repr() << std::endl; close(fd); } @@ -74,6 +75,8 @@ bool Client::readFdIntoBuffer() return false; } + lastActivity = time(NULL); + return true; } @@ -218,6 +221,15 @@ std::string Client::repr() return a.str(); } +bool Client::keepAliveExpired() +{ + if (!authenticated) + return lastActivity + 20 < time(NULL); + + bool result = (lastActivity + (keepalive*10/5)) < time(NULL); + return result; +} + void Client::setReadyForWriting(bool val) { if (disconnecting) diff --git a/client.h b/client.h index ce72ea6..3199336 100644 --- a/client.h +++ b/client.h @@ -6,6 +6,7 @@ #include #include #include +#include #include "forward_declarations.h" @@ -35,6 +36,7 @@ class Client bool readyForReading = true; bool disconnectWhenBytesWritten = false; bool disconnecting = false; + time_t lastActivity = time(NULL); std::string clientid; std::string username; @@ -80,6 +82,7 @@ public: void setReadyForDisconnect() { disconnectWhenBytesWritten = true; } std::string repr(); + bool keepAliveExpired(); }; diff --git a/mainapp.cpp b/mainapp.cpp index e2f537a..9a48dac 100644 --- a/mainapp.cpp +++ b/mainapp.cpp @@ -15,6 +15,7 @@ void do_thread_work(ThreadData *threadData) memset(&events, 0, sizeof (struct epoll_event)*MAX_EVENTS); std::vector packetQueueIn; + time_t lastKeepAliveCheck = time(NULL); while (threadData->running) { @@ -46,7 +47,6 @@ void do_thread_work(ThreadData *threadData) if (!readSuccess) { - std::cout << "Disconnect: " << client->repr() << std::endl; threadData->removeClient(client); continue; } @@ -92,6 +92,19 @@ void do_thread_work(ThreadData *threadData) } } packetQueueIn.clear(); + + try + { + if (lastKeepAliveCheck + 30 < time(NULL)) + { + if (threadData->doKeepAliveCheck()) + lastKeepAliveCheck = time(NULL); + } + } + catch (std::exception &ex) + { + std::cerr << "Error handling keep-alives: " << ex.what() << std::endl; + } } } diff --git a/threaddata.cpp b/threaddata.cpp index 7ce0fb0..7bbc939 100644 --- a/threaddata.cpp +++ b/threaddata.cpp @@ -72,5 +72,28 @@ std::shared_ptr &ThreadData::getSubscriptionStore() return subscriptionStore; } +// TODO: profile how fast hash iteration is. Perhaps having a second list/vector is beneficial? +bool ThreadData::doKeepAliveCheck() +{ + std::unique_lock lock(clients_by_fd_mutex, std::try_to_lock); + if (!lock.owns_lock()) + return false; + + auto it = clients_by_fd.begin(); + while (it != clients_by_fd.end()) + { + Client_p &client = it->second; + if (client->keepAliveExpired()) + { + subscriptionStore->removeClient(client); + it = clients_by_fd.erase(it); + } + else + it++; + } + + return true; +} + diff --git a/threaddata.h b/threaddata.h index a894b0c..4887a43 100644 --- a/threaddata.h +++ b/threaddata.h @@ -40,6 +40,8 @@ public: void removeClient(Client_p client); void removeClient(int fd); std::shared_ptr &getSubscriptionStore(); + + bool doKeepAliveCheck(); }; #endif // THREADDATA_H