Commit 2eed0cfe0e2b0c73567b158ec612c5b34c844cb0
1 parent
22c2cd01
feat(keep-alive): send keep-alive every interval
Also, check if we get a response every ping-request, so we know the connection with the broker is still good.
Showing
3 changed files
with
59 additions
and
8 deletions
src/Connection.cpp
| @@ -396,6 +396,8 @@ bool TrueMQTT::Client::Impl::Connection::connectToAny() | @@ -396,6 +396,8 @@ bool TrueMQTT::Client::Impl::Connection::connectToAny() | ||
| 396 | } | 396 | } |
| 397 | 397 | ||
| 398 | m_socket = socket_connected; | 398 | m_socket = socket_connected; |
| 399 | + m_last_sent_packet = std::chrono::steady_clock::now(); | ||
| 400 | + m_last_received_packet = std::chrono::steady_clock::now(); | ||
| 399 | 401 | ||
| 400 | // Only change the state if no disconnect() has been requested in the mean time. | 402 | // Only change the state if no disconnect() has been requested in the mean time. |
| 401 | if (m_state != State::STOP) | 403 | if (m_state != State::STOP) |
src/Connection.h
| @@ -32,7 +32,7 @@ public: | @@ -32,7 +32,7 @@ public: | ||
| 32 | Connection(TrueMQTT::Client::Impl &impl); | 32 | Connection(TrueMQTT::Client::Impl &impl); |
| 33 | ~Connection(); | 33 | ~Connection(); |
| 34 | 34 | ||
| 35 | - bool send(Packet packet); | 35 | + bool send(Packet packet, bool has_priority = false); |
| 36 | void socketError(); | 36 | void socketError(); |
| 37 | 37 | ||
| 38 | private: | 38 | private: |
| @@ -47,10 +47,11 @@ private: | @@ -47,10 +47,11 @@ private: | ||
| 47 | std::optional<Packet> popSendQueueBlocking(); | 47 | std::optional<Packet> popSendQueueBlocking(); |
| 48 | 48 | ||
| 49 | // Implemented in Packet.cpp | 49 | // Implemented in Packet.cpp |
| 50 | - ssize_t recv(char *buffer, size_t length) const; | 50 | + ssize_t recv(char *buffer, size_t length); |
| 51 | bool recvLoop(); | 51 | bool recvLoop(); |
| 52 | bool sendConnect(); | 52 | bool sendConnect(); |
| 53 | - void sendPacket(Packet &packet) const; | 53 | + bool sendPingRequest(); |
| 54 | + void sendPacket(Packet &packet); | ||
| 54 | 55 | ||
| 55 | enum class State | 56 | enum class State |
| 56 | { | 57 | { |
| @@ -85,4 +86,7 @@ private: | @@ -85,4 +86,7 @@ private: | ||
| 85 | std::deque<Packet> m_send_queue = {}; ///< Queue of packets to send to the broker. | 86 | std::deque<Packet> m_send_queue = {}; ///< Queue of packets to send to the broker. |
| 86 | std::mutex m_send_queue_mutex; ///< Mutex to protect the send queue. | 87 | std::mutex m_send_queue_mutex; ///< Mutex to protect the send queue. |
| 87 | std::condition_variable m_send_queue_cv; ///< Condition variable to wake up the write thread when the send queue is not empty. | 88 | std::condition_variable m_send_queue_cv; ///< Condition variable to wake up the write thread when the send queue is not empty. |
| 89 | + | ||
| 90 | + std::chrono::steady_clock::time_point m_last_sent_packet = {}; ///< Time of the last packet sent to the broker. | ||
| 91 | + std::chrono::steady_clock::time_point m_last_received_packet = {}; ///< Time of the last packet received from the broker. | ||
| 88 | }; | 92 | }; |
src/Packet.cpp
| @@ -14,7 +14,7 @@ | @@ -14,7 +14,7 @@ | ||
| 14 | 14 | ||
| 15 | #include <string.h> | 15 | #include <string.h> |
| 16 | 16 | ||
| 17 | -ssize_t TrueMQTT::Client::Impl::Connection::recv(char *buffer, size_t length) const | 17 | +ssize_t TrueMQTT::Client::Impl::Connection::recv(char *buffer, size_t length) |
| 18 | { | 18 | { |
| 19 | // We idle-check every 100ms if we are requested to stop or if there was | 19 | // We idle-check every 100ms if we are requested to stop or if there was |
| 20 | // an error. This is to prevent the recv() call from blocking forever. | 20 | // an error. This is to prevent the recv() call from blocking forever. |
| @@ -38,6 +38,26 @@ ssize_t TrueMQTT::Client::Impl::Connection::recv(char *buffer, size_t length) co | @@ -38,6 +38,26 @@ ssize_t TrueMQTT::Client::Impl::Connection::recv(char *buffer, size_t length) co | ||
| 38 | return -1; | 38 | return -1; |
| 39 | } | 39 | } |
| 40 | 40 | ||
| 41 | + auto now = std::chrono::steady_clock::now(); | ||
| 42 | + | ||
| 43 | + // Send a keep-alive to the broker if we haven't sent anything for a while. | ||
| 44 | + if (m_last_sent_packet + m_impl.m_keep_alive_interval < now) | ||
| 45 | + { | ||
| 46 | + // Force updating the time, as it might be a while before the ping actually leaves | ||
| 47 | + // the send queue. And we don't need to send more than one for the whole interval. | ||
| 48 | + m_last_sent_packet = std::chrono::steady_clock::now(); | ||
| 49 | + sendPingRequest(); | ||
| 50 | + } | ||
| 51 | + | ||
| 52 | + // As the broker should send a ping every keep-alive interval, we really should | ||
| 53 | + // see something no later than every 1.5 times the keep-alive interval. If not, | ||
| 54 | + // we assume the connection is dead. | ||
| 55 | + if (m_last_received_packet + m_impl.m_keep_alive_interval * 15 / 10 < now) | ||
| 56 | + { | ||
| 57 | + LOG_ERROR(&m_impl, "Connection timed out"); | ||
| 58 | + return -1; | ||
| 59 | + } | ||
| 60 | + | ||
| 41 | if (ret == 0) | 61 | if (ret == 0) |
| 42 | { | 62 | { |
| 43 | continue; | 63 | continue; |
| @@ -213,18 +233,24 @@ bool TrueMQTT::Client::Impl::Connection::recvLoop() | @@ -213,18 +233,24 @@ bool TrueMQTT::Client::Impl::Connection::recvLoop() | ||
| 213 | 233 | ||
| 214 | break; | 234 | break; |
| 215 | } | 235 | } |
| 236 | + case Packet::PacketType::PINGRESP: | ||
| 237 | + { | ||
| 238 | + LOG_DEBUG(&m_impl, "Received PINGRESP"); | ||
| 239 | + break; | ||
| 240 | + } | ||
| 216 | default: | 241 | default: |
| 217 | LOG_ERROR(&m_impl, "Received unexpected packet type " + std::string(magic_enum::enum_name(packet_type)) + " from broker, closing connection"); | 242 | LOG_ERROR(&m_impl, "Received unexpected packet type " + std::string(magic_enum::enum_name(packet_type)) + " from broker, closing connection"); |
| 218 | return false; | 243 | return false; |
| 219 | } | 244 | } |
| 220 | 245 | ||
| 246 | + m_last_received_packet = std::chrono::steady_clock::now(); | ||
| 221 | return true; | 247 | return true; |
| 222 | } | 248 | } |
| 223 | 249 | ||
| 224 | -bool TrueMQTT::Client::Impl::Connection::send(Packet packet) | 250 | +bool TrueMQTT::Client::Impl::Connection::send(Packet packet, bool has_priority) |
| 225 | { | 251 | { |
| 226 | // Push back if the internal queue gets too big. | 252 | // Push back if the internal queue gets too big. |
| 227 | - if (m_send_queue.size() > m_impl.m_send_queue_size) | 253 | + if (!has_priority && m_send_queue.size() > m_impl.m_send_queue_size) |
| 228 | { | 254 | { |
| 229 | return false; | 255 | return false; |
| 230 | } | 256 | } |
| @@ -254,7 +280,15 @@ bool TrueMQTT::Client::Impl::Connection::send(Packet packet) | @@ -254,7 +280,15 @@ bool TrueMQTT::Client::Impl::Connection::send(Packet packet) | ||
| 254 | // Add the packet to the queue. | 280 | // Add the packet to the queue. |
| 255 | { | 281 | { |
| 256 | std::scoped_lock lock(m_send_queue_mutex); | 282 | std::scoped_lock lock(m_send_queue_mutex); |
| 257 | - m_send_queue.push_back(std::move(packet)); | 283 | + if (has_priority) |
| 284 | + { | ||
| 285 | + m_send_queue.push_front(std::move(packet)); | ||
| 286 | + } | ||
| 287 | + else | ||
| 288 | + { | ||
| 289 | + m_send_queue.push_back(std::move(packet)); | ||
| 290 | + } | ||
| 291 | + | ||
| 258 | } | 292 | } |
| 259 | // Notify the write thread that there is a new packet. | 293 | // Notify the write thread that there is a new packet. |
| 260 | m_send_queue_cv.notify_one(); | 294 | m_send_queue_cv.notify_one(); |
| @@ -262,7 +296,7 @@ bool TrueMQTT::Client::Impl::Connection::send(Packet packet) | @@ -262,7 +296,7 @@ bool TrueMQTT::Client::Impl::Connection::send(Packet packet) | ||
| 262 | return true; | 296 | return true; |
| 263 | } | 297 | } |
| 264 | 298 | ||
| 265 | -void TrueMQTT::Client::Impl::Connection::sendPacket(Packet &packet) const | 299 | +void TrueMQTT::Client::Impl::Connection::sendPacket(Packet &packet) |
| 266 | { | 300 | { |
| 267 | if (m_state != State::AUTHENTICATING && m_state != State::CONNECTED) | 301 | if (m_state != State::AUTHENTICATING && m_state != State::CONNECTED) |
| 268 | { | 302 | { |
| @@ -290,6 +324,8 @@ void TrueMQTT::Client::Impl::Connection::sendPacket(Packet &packet) const | @@ -290,6 +324,8 @@ void TrueMQTT::Client::Impl::Connection::sendPacket(Packet &packet) const | ||
| 290 | } | 324 | } |
| 291 | packet.m_write_offset += res; | 325 | packet.m_write_offset += res; |
| 292 | } | 326 | } |
| 327 | + | ||
| 328 | + m_last_sent_packet = std::chrono::steady_clock::now(); | ||
| 293 | } | 329 | } |
| 294 | 330 | ||
| 295 | bool TrueMQTT::Client::Impl::Connection::sendConnect() | 331 | bool TrueMQTT::Client::Impl::Connection::sendConnect() |
| @@ -329,6 +365,15 @@ bool TrueMQTT::Client::Impl::Connection::sendConnect() | @@ -329,6 +365,15 @@ bool TrueMQTT::Client::Impl::Connection::sendConnect() | ||
| 329 | return send(std::move(packet)); | 365 | return send(std::move(packet)); |
| 330 | } | 366 | } |
| 331 | 367 | ||
| 368 | +bool TrueMQTT::Client::Impl::Connection::sendPingRequest() | ||
| 369 | +{ | ||
| 370 | + LOG_TRACE(&m_impl, "Sending PINGREQ packet"); | ||
| 371 | + | ||
| 372 | + Packet packet(Packet::PacketType::PINGREQ, 0); | ||
| 373 | + | ||
| 374 | + return send(std::move(packet), true); | ||
| 375 | +} | ||
| 376 | + | ||
| 332 | bool TrueMQTT::Client::Impl::sendPublish(const std::string &topic, const std::string &message, bool retain) | 377 | bool TrueMQTT::Client::Impl::sendPublish(const std::string &topic, const std::string &message, bool retain) |
| 333 | { | 378 | { |
| 334 | LOG_TRACE(this, "Sending PUBLISH packet to topic '" + topic + "': " + message + " (" + (retain ? "retained" : "not retained") + ")"); | 379 | LOG_TRACE(this, "Sending PUBLISH packet to topic '" + topic + "': " + message + " (" + (retain ? "retained" : "not retained") + ")"); |