Dipro Sen
Dipro Sen

Reputation: 4670

boost asio stateful server design

I am writing a Server application on boost::asio

  1. Here I instantiate a Server that owns an io_service
  2. Server is started by server.start() method which calls Server::accept() and that creates a new Session even before it gets a new connection
  3. Calls _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

Answers (1)

Tanner Sansbury
Tanner Sansbury

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

Related Questions