Reputation: 11890
The following code is based on boost documentation example at http://www.boost.org/doc/libs/1_46_1/doc/html/boost_asio/example/echo/async_tcp_echo_server.cpp. I just changed it a little so that I can run the server in a different thread and quit after running for a few seconds.
This code works as expected. When a client connects and sends some text, a copy is echoed back to the client.
There is just one problem. There is one instance of class session
that never gets deleted. If you run the code, you can witness the number of times the constructor and the destructor is called. It is always the last session
instance that does not get deleted.
Wondering if there is a simple way to fix this. Perhaps I can use smart_ptr class.
Note that I have to use boost library 1.46. There are some other examples I found that are based on the newer library version. But these examples don't compile under older boost library. Regards.
#include <cstdlib>
#include <iostream>
#include <vector>
#include <boost/bind.hpp>
#include <boost/asio.hpp>
#include <boost/thread.hpp>
using boost::asio::ip::tcp;
class session
{
public:
session(boost::asio::io_service& io_service)
: socket_(io_service)
{
char buf[100]; sprintf(buf, "%p", this);
std::cout << "Session created: " << buf << std::endl;
}
~session() {
char buf[100]; sprintf(buf, "%p", this);
std::cout << "Session destroyed: " << buf << std::endl;
}
tcp::socket& socket()
{
return socket_;
}
void start()
{
socket_.async_read_some(boost::asio::buffer(data_, max_length),
boost::bind(&session::handle_read, this,
boost::asio::placeholders::error,
boost::asio::placeholders::bytes_transferred));
}
void handle_read(const boost::system::error_code& error,
size_t bytes_transferred)
{
if (!error)
{
boost::asio::async_write(socket_,
boost::asio::buffer(data_, bytes_transferred),
boost::bind(&session::handle_write, this,
boost::asio::placeholders::error));
}
else
{
delete this;
}
}
void handle_write(const boost::system::error_code& error)
{
if (!error)
{
socket_.async_read_some(boost::asio::buffer(data_, max_length),
boost::bind(&session::handle_read, this,
boost::asio::placeholders::error,
boost::asio::placeholders::bytes_transferred));
}
else
{
delete this;
}
}
private:
tcp::socket socket_;
enum { max_length = 1024 };
char data_[max_length];
};
class server
{
public:
server(boost::asio::io_service& io_service, short port)
: io_service_(io_service),
acceptor_(io_service, tcp::endpoint(tcp::v4(), port))
{
session* new_session = new session(io_service_);
acceptor_.async_accept(new_session->socket(),
boost::bind(&server::handle_accept, this, new_session,
boost::asio::placeholders::error));
}
~server() {
}
void handle_accept(session* new_session,
const boost::system::error_code& error)
{
if (!error)
{
new_session->start(); // ownership passed
new_session = new session(io_service_);
acceptor_.async_accept(new_session->socket(),
boost::bind(&server::handle_accept, this, new_session,
boost::asio::placeholders::error));
}
else
{
std::cout << "Error in HA: " << error.message() << std::endl;
}
}
private:
boost::asio::io_service& io_service_;
tcp::acceptor acceptor_;
};
/*
int main(int argc, char* argv[])
{
try
{
if (argc != 2)
{
std::cerr << "Usage: async_tcp_echo_server <port>\n";
return 1;
}
boost::asio::io_service io_service;
using namespace std; // For atoi.
server s(io_service, atoi(argv[1]));
io_service.run();
}
catch (std::exception& e)
{
std::cerr << "Exception: " << e.what() << "\n";
}
return 0;
}
*/
static void serverThread(boost::asio::io_service* io_service) {
server s(*io_service, 5000);
io_service->run();
}
int main(int argc, char* argv[])
{
boost::asio::io_service io_service;
boost::thread t(serverThread, &io_service);
::sleep(10);
io_service.stop();
t.join();
std::cout << "Quitting" << std::endl;
return 0;
}
Upvotes: 1
Views: 661
Reputation: 6901
The one that does not get deleted is the one that gets passed to the asynchronous accept handler i.e handler_accept
. I would recommend heavily to make use of smart pointer. shared_ptr
is a good candidate here:
#include <cstdlib>
#include <iostream>
#include <vector>
#include <thread>
#include <boost/bind.hpp>
#include <boost/asio.hpp>
#include <memory>
using boost::asio::ip::tcp;
class session: public std::enable_shared_from_this<session>
{
public:
session(boost::asio::io_service& io_service)
: socket_(io_service)
{
char buf[100]; sprintf(buf, "%p", this);
std::cout << "Session created: " << buf << std::endl;
}
~session() {
char buf[100]; sprintf(buf, "%p", this);
std::cout << "Session destroyed: " << buf << std::endl;
}
tcp::socket& socket()
{
return socket_;
}
void start()
{
socket_.async_read_some(boost::asio::buffer(data_, max_length),
boost::bind(&session::handle_read, shared_from_this(),
boost::asio::placeholders::error,
boost::asio::placeholders::bytes_transferred));
}
void handle_read(const boost::system::error_code& error,
size_t bytes_transferred)
{
if (!error)
{
boost::asio::async_write(socket_,
boost::asio::buffer(data_, bytes_transferred),
boost::bind(&session::handle_write, shared_from_this(),
boost::asio::placeholders::error));
}
}
void handle_write(const boost::system::error_code& error)
{
if (!error)
{
socket_.async_read_some(boost::asio::buffer(data_, max_length),
boost::bind(&session::handle_read, shared_from_this(),
boost::asio::placeholders::error,
boost::asio::placeholders::bytes_transferred));
}
}
private:
tcp::socket socket_;
enum { max_length = 1024 };
char data_[max_length];
};
using session_ptr = std::shared_ptr<session>;
class server
{
public:
server(boost::asio::io_service& io_service, short port)
: io_service_(io_service),
acceptor_(io_service, tcp::endpoint(tcp::v4(), port))
{
auto new_session = std::make_shared<session>(io_service_);
acceptor_.async_accept(new_session->socket(),
boost::bind(&server::handle_accept, this, new_session,
boost::asio::placeholders::error));
}
~server() {
}
void handle_accept(session_ptr new_session,
const boost::system::error_code& error)
{
if (!error)
{
new_session->start(); // ownership passed
new_session.reset();
new_session = std::make_shared<session>(io_service_);
acceptor_.async_accept(new_session->socket(),
boost::bind(&server::handle_accept, this, new_session,
boost::asio::placeholders::error));
}
else
{
std::cout << "Error in HA: " << error.message() << std::endl;
}
}
private:
boost::asio::io_service& io_service_;
tcp::acceptor acceptor_;
};
/*
int main(int argc, char* argv[])
{
try
{
if (argc != 2)
{
std::cerr << "Usage: async_tcp_echo_server <port>\n";
return 1;
}
boost::asio::io_service io_service;
using namespace std; // For atoi.
server s(io_service, atoi(argv[1]));
io_service.run();
}
catch (std::exception& e)
{
std::cerr << "Exception: " << e.what() << "\n";
}
return 0;
}
*/
static void serverThread(boost::asio::io_service* io_service) {
server s(*io_service, 5000);
io_service->run();
}
int main(int argc, char* argv[])
{
boost::asio::io_service io_service;
std::thread t(serverThread, &io_service);
::sleep(10);
io_service.stop();
t.join();
std::cout << "Quitting" << std::endl;
return 0;
}
I have made use of c++11 features, namely std::thread
and std::shared_ptr
. std::enable_shared_from_this
gives you the ability to create a shared_ptr
object of this
. Checkout how it is used inside async function calls of session
class.
Also, looking at the code it makes me feel that you are expecting the handlers to be called with some error code once you call io_service::stop
? No, that is not the case. It will simple calls scheduler::shutdown
which simply destroys the async handlers, nothing else.
Upvotes: 2