Reputation: 4670
I am writing a Server application on boost::asio
Server
that owns an io_service
server.start()
method which calls Server::accept()
and that creates a new Session
even before it gets a new connection_acceptor.async_accept
thats set a callback that calls Session::handler()
method which does the handshaking.Now is it okay to create a socket even before getting a new client ?
and in this code the Session is auto destructed after writing an "Hello "
Message which is Okay in case of HTTP, But I want to carry on a Stateful Communication. so socket must async_wait
for reading after it just wrote this "Hallo "
. also I'd like to know whether this design is okay ? or it Has any design flaw.
Here goes My Compilable Code
~
class Session: public boost::enable_shared_from_this<Session>, private boost::noncopyable{
private:
size_t _id;
boost::asio::ip::tcp::socket _socket;
public:
typedef boost::shared_ptr<Session> pointer;
static pointer create(boost::asio::io_service& ios){
return pointer(new Session(ios));
}
private:
explicit Session(boost::asio::io_service& ios): _socket(ios){
static size_t counter = 0;
_id = counter++;
std::cout << ">> Session " << id() << " constructing" << std::endl;
}
public:
void handler(const boost::system::error_code &ec){
const std::string message = (boost::format("HTTP/1.1 200 OK\r\nContent-Length: %2%\r\n\r\nHello, %1%!") % id() % (7+boost::lexical_cast<std::string>(id()).length())).str();
if(!ec){
boost::asio::async_write(_socket, boost::asio::buffer(message), boost::bind(&Session::write_handler, this));
}else{
std::cout << ec.message() << std::endl;
}
}
void write_handler(){
}
size_t id() const{
return _id;
}
boost::asio::ip::tcp::socket& socket(){
return _socket;
}
virtual ~Session(){
std::cout << ">> Session " << id() << " destructing" << std::endl;
}
};
class Server: public boost::enable_shared_from_this<Server>, private boost::noncopyable{
private:
boost::asio::io_service _ios;
boost::asio::ip::tcp::acceptor _acceptor;
public:
explicit Server(boost::asio::ip::tcp::endpoint& endpoint):_acceptor(_ios, endpoint){
}
void start(){
accept();
_ios.run();
}
void accept(){
std::cout << "accepting " << std::endl;;
Session::pointer session = Session::create(_ios);
_acceptor.async_accept(session->socket(), boost::bind(&Server::handler, this, session, boost::asio::placeholders::error));
}
void handler(Session::pointer session, const boost::system::error_code &ec){
if(!ec){
session->handler(ec);
}else{
//possible destroy session ? but how to destroy a shared pointer ?
}
accept();
}
};
int main(){
const unsigned int port = 5050;
boost::asio::ip::tcp::endpoint endpoint(boost::asio::ip::tcp::v4(), port);
Server server(endpoint);
server.start();
return 0;
}
Upvotes: 3
Views: 1917
Reputation: 51891
The overall design looks okay, but there are a few implementation errors:
Session::handler()
: message
, the underlying buffer provided async_write()
, may go out of scope before async_write()
is invoked. This is a violation of the guarantee required by the API. Consider making message
a member variable for Session
, or make it static
within the member function. This is the relevant excerpt from the documentation:
Although the buffers object may be copied as necessary, ownership of the underlying memory blocks is retained by the caller, which must guarantee that they remain valid until the handler is called. Here is the relevant excerpt from the documentation:
For objects inheriting from boost::enable_shared_from_this
, use shared_from_this()
instead of the this
pointer when passing the instance handle to boost::bind
. Otherwise, it is possible that the object instance pointed to by this
may be deleted prior to the handler running.
Also, to address the question in the code comment within the Server::handler()
, if an error occurs, the Session
will be deleted once the handler returns since it is managed via a boost::shared_ptr
.
Upvotes: 3