c++ - Boost.Asio TCP moved-to socket destructor not enough to cleanly close? -
consider test program :
#include <boost/asio/io_service.hpp> #include <boost/asio/ip/tcp.hpp> #include <functional> #include <iostream> static void callback (boost::asio::ip::tcp::socket && socket) { //boost::asio::ip::tcp::socket new_socket = std::move(socket); std::cout << "accepted" << std::endl; } static void on_accept (boost::asio::ip::tcp::acceptor & acceptor, boost::asio::ip::tcp::socket & socket, boost::system::error_code const & error) { if (error) { std::cerr << error << ' ' << error.message() << std::endl; return ; } callback(std::move(socket)); acceptor.async_accept ( socket, std::bind(on_accept, std::ref(acceptor), std::ref(socket), std::placeholders::_1) ); } int main () { boost::asio::io_service service; boost::asio::io_service::work work { service }; boost::asio::ip::tcp::acceptor acceptor { service }; boost::asio::ip::tcp::socket socket { service }; boost::asio::ip::tcp::endpoint endpoint { boost::asio::ip::tcp::v4(), 5555 }; boost::system::error_code ec; using socket_base = boost::asio::socket_base; auto option = socket_base::reuse_address { false }; if (acceptor.open(endpoint.protocol(), ec) || acceptor.set_option(option, ec) || acceptor.bind(endpoint, ec) || acceptor.listen(socket_base::max_connections, ec) || acceptor.is_open() == false) return 1; acceptor.async_accept ( socket, std::bind(on_accept, std::ref(acceptor), std::ref(socket), std::placeholders::_1) ); service.run(); }
when connect client it, error :
accepted
system:1 incorrect function
(the on_accept()
function called error code when socket
object callback()
function destroyed).
also, client not disconnected @ all.
if uncomment line in callback()
function, works fine, no error message , client disconnected expected.
now environment settings, i'm under windows 8.1, using mingw-w64 v4.9.2 compiler boost.asio v1.58.0 compiled same compiler.
the command line used compile file follow :
$ g++ -std=c++14 -ic:/c++/boost/1.58.0 main.cpp -lc:/c++/boost/1.58.0/lib -lboost_system-mgw49-mt-1_58 -lwsock32 -lws2_32 -o test.exe
note using boost 1.57.0 results in same behavior.
i can remove commented line completely, , use :
static void callback (boost::asio::ip::tcp::socket && socket) { std::cout << "accepted" << std::endl; socket.shutdown(socket.shutdown_both); socket.close(); }
and program behave correctly too.
so, how come need add steps not error here ? iirc behavior wasn't there couple of months ago when first tried program.
the code creates single socket, automatic variable lifetime end once main()
returns. std::move(socket)
returns xvalue can provided socket's move constructor; not construct socket.
to resolve this, consider changing callback()
signature accepting socket via value, allowing compiler invoke move-constructor when given xvalue. change:
static void callback (boost::asio::ip::tcp::socket && socket)
to:
static void callback (boost::asio::ip::tcp::socket socket)
overall, flow of code follows:
void callback(socket&&); // rvalue reference. void on_accept(acceptor&, socket&, ...) // lvalue reference. { ... callback(static_cast<socket&&>(socket)); // cast xvalue. acceptor.async_accept(socket, std::bind(&on_accept, std::ref:acceptor), std::ref(socket), // lvalue reference. ...); } int main() { boost::asio::io_service io_service; boost::asio::io_service::work work(io_service); boost::asio::ip::tcp::acceptor acceptor(io_service); boost::asio::ip::tcp::socket socket(io_service); // constructor. ... acceptor.async_accept(socket, std::bind(&on_accept, std::ref:acceptor), std::ref(socket), // lvalue reference. ...); io_service.run(); }
upon accepting first connection, socket in main()
open. on_accept()
function invokes callback()
xvalue, , not change state of socket. async_accept()
operation initiated using open socket, resulting in operation's failure. async_accept()
operation fails, invoking on_accept()
return early, stopping call chain. io_service::work
attached io_service
, execution never returns io_service::run()
, preventing main()
returning , destroying socket. final result no more connections accepted (no async_accept()
operations) , client not disconnected (socket
never destroyed).
when callback()
changes state of socket close, issue no longer present pre-condition async_accept()
met. other examples meet pre-condition because:
- uncommenting 1 line results in move-constructor being invoking. moved-from socket have same state if constructed using
socket(io_service&)
constructor. - the socket explicitly closed via
socket.close()
.
Comments
Post a Comment