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

Popular posts from this blog

php - failed to open stream: HTTP request failed! HTTP/1.0 400 Bad Request -

java - How to filter a backspace keyboard input -

java - Show Soft Keyboard when EditText Appears -