Commit f8e062bf9b371cc1cfd1e8af59b2aaa46df006e6
1 parent
fce0beff
Simple keep-alive mechanism
Showing
5 changed files
with
54 additions
and
1 deletions
client.cpp
| ... | ... | @@ -17,6 +17,7 @@ Client::Client(int fd, ThreadData_p threadData) : |
| 17 | 17 | |
| 18 | 18 | Client::~Client() |
| 19 | 19 | { |
| 20 | + std::cout << "Removing client: " << repr() << std::endl; | |
| 20 | 21 | close(fd); |
| 21 | 22 | } |
| 22 | 23 | |
| ... | ... | @@ -74,6 +75,8 @@ bool Client::readFdIntoBuffer() |
| 74 | 75 | return false; |
| 75 | 76 | } |
| 76 | 77 | |
| 78 | + lastActivity = time(NULL); | |
| 79 | + | |
| 77 | 80 | return true; |
| 78 | 81 | } |
| 79 | 82 | |
| ... | ... | @@ -218,6 +221,15 @@ std::string Client::repr() |
| 218 | 221 | return a.str(); |
| 219 | 222 | } |
| 220 | 223 | |
| 224 | +bool Client::keepAliveExpired() | |
| 225 | +{ | |
| 226 | + if (!authenticated) | |
| 227 | + return lastActivity + 20 < time(NULL); | |
| 228 | + | |
| 229 | + bool result = (lastActivity + (keepalive*10/5)) < time(NULL); | |
| 230 | + return result; | |
| 231 | +} | |
| 232 | + | |
| 221 | 233 | void Client::setReadyForWriting(bool val) |
| 222 | 234 | { |
| 223 | 235 | if (disconnecting) | ... | ... |
client.h
| ... | ... | @@ -6,6 +6,7 @@ |
| 6 | 6 | #include <vector> |
| 7 | 7 | #include <mutex> |
| 8 | 8 | #include <iostream> |
| 9 | +#include <time.h> | |
| 9 | 10 | |
| 10 | 11 | #include "forward_declarations.h" |
| 11 | 12 | |
| ... | ... | @@ -35,6 +36,7 @@ class Client |
| 35 | 36 | bool readyForReading = true; |
| 36 | 37 | bool disconnectWhenBytesWritten = false; |
| 37 | 38 | bool disconnecting = false; |
| 39 | + time_t lastActivity = time(NULL); | |
| 38 | 40 | |
| 39 | 41 | std::string clientid; |
| 40 | 42 | std::string username; |
| ... | ... | @@ -80,6 +82,7 @@ public: |
| 80 | 82 | void setReadyForDisconnect() { disconnectWhenBytesWritten = true; } |
| 81 | 83 | |
| 82 | 84 | std::string repr(); |
| 85 | + bool keepAliveExpired(); | |
| 83 | 86 | |
| 84 | 87 | }; |
| 85 | 88 | ... | ... |
mainapp.cpp
| ... | ... | @@ -15,6 +15,7 @@ void do_thread_work(ThreadData *threadData) |
| 15 | 15 | memset(&events, 0, sizeof (struct epoll_event)*MAX_EVENTS); |
| 16 | 16 | |
| 17 | 17 | std::vector<MqttPacket> packetQueueIn; |
| 18 | + time_t lastKeepAliveCheck = time(NULL); | |
| 18 | 19 | |
| 19 | 20 | while (threadData->running) |
| 20 | 21 | { |
| ... | ... | @@ -46,7 +47,6 @@ void do_thread_work(ThreadData *threadData) |
| 46 | 47 | |
| 47 | 48 | if (!readSuccess) |
| 48 | 49 | { |
| 49 | - std::cout << "Disconnect: " << client->repr() << std::endl; | |
| 50 | 50 | threadData->removeClient(client); |
| 51 | 51 | continue; |
| 52 | 52 | } |
| ... | ... | @@ -92,6 +92,19 @@ void do_thread_work(ThreadData *threadData) |
| 92 | 92 | } |
| 93 | 93 | } |
| 94 | 94 | packetQueueIn.clear(); |
| 95 | + | |
| 96 | + try | |
| 97 | + { | |
| 98 | + if (lastKeepAliveCheck + 30 < time(NULL)) | |
| 99 | + { | |
| 100 | + if (threadData->doKeepAliveCheck()) | |
| 101 | + lastKeepAliveCheck = time(NULL); | |
| 102 | + } | |
| 103 | + } | |
| 104 | + catch (std::exception &ex) | |
| 105 | + { | |
| 106 | + std::cerr << "Error handling keep-alives: " << ex.what() << std::endl; | |
| 107 | + } | |
| 95 | 108 | } |
| 96 | 109 | } |
| 97 | 110 | ... | ... |
threaddata.cpp
| ... | ... | @@ -72,5 +72,28 @@ std::shared_ptr<SubscriptionStore> &ThreadData::getSubscriptionStore() |
| 72 | 72 | return subscriptionStore; |
| 73 | 73 | } |
| 74 | 74 | |
| 75 | +// TODO: profile how fast hash iteration is. Perhaps having a second list/vector is beneficial? | |
| 76 | +bool ThreadData::doKeepAliveCheck() | |
| 77 | +{ | |
| 78 | + std::unique_lock<std::mutex> lock(clients_by_fd_mutex, std::try_to_lock); | |
| 79 | + if (!lock.owns_lock()) | |
| 80 | + return false; | |
| 81 | + | |
| 82 | + auto it = clients_by_fd.begin(); | |
| 83 | + while (it != clients_by_fd.end()) | |
| 84 | + { | |
| 85 | + Client_p &client = it->second; | |
| 86 | + if (client->keepAliveExpired()) | |
| 87 | + { | |
| 88 | + subscriptionStore->removeClient(client); | |
| 89 | + it = clients_by_fd.erase(it); | |
| 90 | + } | |
| 91 | + else | |
| 92 | + it++; | |
| 93 | + } | |
| 94 | + | |
| 95 | + return true; | |
| 96 | +} | |
| 97 | + | |
| 75 | 98 | |
| 76 | 99 | ... | ... |