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