diff --git a/CHANGELOG.md b/CHANGELOG.md index 9342c9b3..c0c7f025 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,7 @@ Version 147: WebSocket: * control callback is copied or moved +* Send idle pings in advanced servers -------------------------------------------------------------------------------- diff --git a/example/advanced/server-flex/advanced_server_flex.cpp b/example/advanced/server-flex/advanced_server_flex.cpp index 30d6f462..dcffdef8 100644 --- a/example/advanced/server-flex/advanced_server_flex.cpp +++ b/example/advanced/server-flex/advanced_server_flex.cpp @@ -234,6 +234,7 @@ class websocket_session } boost::beast::multi_buffer buffer_; + char ping_state_ = 0; protected: boost::asio::strand< @@ -255,6 +256,15 @@ public: void do_accept(http::request
> req) { + // Set the control callback. This will be called + // on every incoming ping, pong, and close frame. + derived().ws().control_callback( + std::bind( + &websocket_session::on_control_callback, + this, + std::placeholders::_1, + std::placeholders::_2)); + // Set the timer timer_.expires_after(std::chrono::seconds(15)); @@ -269,27 +279,6 @@ public: std::placeholders::_1))); } - // Called when the timer expires. - void - on_timer(boost::system::error_code ec) - { - if(ec && ec != boost::asio::error::operation_aborted) - return fail(ec, "timer"); - - // Verify that the timer really expired since the deadline may have moved. - if(timer_.expiry() <= std::chrono::steady_clock::now()) - derived().do_timeout(); - - // Wait on the timer - timer_.async_wait( - boost::asio::bind_executor( - strand_, - std::bind( - &websocket_session::on_timer, - derived().shared_from_this(), - std::placeholders::_1))); - } - void on_accept(boost::system::error_code ec) { @@ -304,12 +293,106 @@ public: do_read(); } + // Called when the timer expires. + void + on_timer(boost::system::error_code ec) + { + if(ec && ec != boost::asio::error::operation_aborted) + return fail(ec, "timer"); + + // See if the timer really expired since the deadline may have moved. + if(timer_.expiry() <= std::chrono::steady_clock::now()) + { + // If this is the first time the timer expired, + // send a ping to see if the other end is there. + if(derived().ws().is_open() && ping_state_ == 0) + { + // Note that we are sending a ping + ping_state_ = 1; + + // Set the timer + timer_.expires_after(std::chrono::seconds(15)); + + // Now send the ping + derived().ws().async_ping({}, + boost::asio::bind_executor( + strand_, + std::bind( + &websocket_session::on_ping, + derived().shared_from_this(), + std::placeholders::_1))); + } + else + { + // The timer expired while trying to handshake, + // or we sent a ping and it never completed or + // we never got back a control frame, so close. + + derived().do_timeout(); + return; + } + } + + // Wait on the timer + timer_.async_wait( + boost::asio::bind_executor( + strand_, + std::bind( + &websocket_session::on_timer, + derived().shared_from_this(), + std::placeholders::_1))); + } + + // Called to indicate activity from the remote peer + void + activity() + { + // Note that the connection is alive + ping_state_ = 0; + + // Set the timer + timer_.expires_after(std::chrono::seconds(15)); + } + + // Called after a ping is sent. + void + on_ping(boost::system::error_code ec) + { + // Happens when the timer closes the socket + if(ec == boost::asio::error::operation_aborted) + return; + + if(ec) + return fail(ec, "ping"); + + // Note that the ping was sent. + if(ping_state_ == 1) + { + ping_state_ = 2; + } + else + { + // ping_state_ could have been set to 0 + // if an incoming control frame was received + // at exactly the same time we sent a ping. + BOOST_ASSERT(ping_state_ == 0); + } + } + + void + on_control_callback( + websocket::frame_type kind, + boost::beast::string_view payload) + { + boost::ignore_unused(kind, payload); + + // Note that there is activity + activity(); + } + void do_read() { - // Set the timer - timer_.expires_after(std::chrono::seconds(15)); - // Read a message into our buffer derived().ws().async_read( buffer_, @@ -340,6 +423,9 @@ public: if(ec) fail(ec, "read"); + // Note that there is activity + activity(); + // Echo the message derived().ws().text(derived().ws().got_text()); derived().ws().async_write( diff --git a/example/advanced/server/advanced_server.cpp b/example/advanced/server/advanced_server.cpp index 95bf8de6..523094b2 100644 --- a/example/advanced/server/advanced_server.cpp +++ b/example/advanced/server/advanced_server.cpp @@ -219,6 +219,7 @@ class websocket_session : public std::enable_shared_from_this