Boost :: asio acceptor reopen and read asynchronously after EOF

Unfortunately, I came to the conclusion that I cannot understand exactly how it is supposed to use boost :: asio :: ip :: tcp :: acceptor. The TCPReader class is designed as a single line, and the header declares the following as private.

void runInThread(); void read_handler(const boost::system::error_code &ec, std::size_t bytes_transferred); void accept_handler(const boost::system::error_code &ec); boost::asio::io_service io_service; boost::asio::io_service::work runningService; boost::asio::ip::tcp::socket sock; boost::asio::ip::tcp::acceptor acceptor; bool isConnected; 

<<

 TCPReader::TCPReader() : sock(io_service), runningService(io_service), acceptor(io_service, boost::asio::ip::tcp::endpoint(boost::asio::ip::tcp::v4(), TCPPort)){ this->isConnected = false; acceptor.async_accept(sock, boost::bind(&TCPReader::accept_handler, this, boost::asio::placeholders::error)); boost::thread thread(boost::bind(&TCPReader::runInThread, this)); } void TCPReader::runInThread(){ io_service.run(); } 

accept_handler, which launches read_handler, and it all works like a charm until read_handler gets an EOF or other error. I want the acceptor to return to a state where it can accept connections, as it did after initialization. I tried close() or cancel() with the following open() , listen() and async_accept (as in the constructor). But all this does not seem to work. Would love your help.

0
source share
1 answer

The error occurs during the sock operation, not the acceptor . Therefore, the acceptor state acceptor not be affected. It just requires initiating the async_accept operation with sock in the initial closed state.

Here is a complete basic example that listens on port 12345:

 #include <iostream> #include <boost/array.hpp> #include <boost/asio.hpp> using boost::asio::ip::tcp; class tcp_reader { public: tcp_reader(boost::asio::io_service& io_service) : io_service_(io_service), socket_(io_service), acceptor_(io_service, tcp::endpoint(tcp::v4(), 12345)) { accept_connection(); } private: void accept_connection() { std::cout << "accepting connection" << std::endl; // Verify socket is in a closed state. socket_.close(); // On success or failure, acceptor will open() socket_. acceptor_.async_accept(socket_, boost::bind(&tcp_reader::handle_accept, this, boost::asio::placeholders::error)); } void handle_accept(const boost::system::error_code& error) { // On error, return early. if (error) { std::cout << "handle_accept: " << error.message() << std::endl; return; } // Start reading from socket. read(); } void read() { std::cout << "reading from socket" << std::endl; async_read(socket_, boost::asio::buffer(buffer_), boost::asio::transfer_at_least(1), boost::bind(&tcp_reader::handle_read, this, boost::asio::placeholders::error, boost::asio::placeholders::bytes_transferred)); } void handle_read(const boost::system::error_code& error, std::size_t bytes_transferred) { // On error, go back to listening for a new connection. if (error) { std::cout << "handle_read: " << error.message() << std::endl; accept_connection(); return; } // Output read data. std::cout.write(&buffer_[0], bytes_transferred); // Read data, so read some more. read(); } private: boost::asio::io_service& io_service_; tcp::socket socket_; tcp::acceptor acceptor_; boost::array<char, 1024> buffer_; }; int main() { boost::asio::io_service io_service; tcp_reader reader(io_service); io_service.run(); } 

When using it, I ran it on one terminal and connected to port 12345, sent messages, killed the connection, and then restored the connection. The output from the server console was as follows:

  [ twsansbury@localhost ] $ ./a.out 
 accepting connection
 reading from socket
 hello
 reading from socket
 goodbye
 reading from socket
 handle_read: End of file
 accepting connection
 reading from socket
 oh oh no
 reading from socket
 handle_read: End of file
 accepting connection
 ctrl + c 

and client console:

  [ twsansbury@localhost ] $ nc 127.0.0.1 12345
 hello        
 goodbye 
 ctrl + c
 [ twsansbury@localhost ] $ nc 127.0.0.1 12345
 oh oh no
 ctrl + c 

One behavioral detail to be aware of is that although the async_accept operation cannot be deferred to acceptor_ , connections are still in the queue. Thus, if the connection has already been accepted and another client is trying to connect, the last client will have its connection pending. If this is not the desired behavior, then the acceptor must transition from the listening state through close() . This response diagram displays the state of transitions and describes some of the acceptor behavior in more detail.

+3
source

All Articles