boost.png (6897 bytes)

PrevUpHomeNext

Sockets

Simple Finger Service

For sockets, Giallo provides connection<>, connector<> and acceptor<> classes.

In most socket code, there is a one-time design choice to use blocking, non-blocking (reactive) or asynchronous (proactive) system calls. In Giallo this choice becomes an implementation detail, that can be changed at anytime without affecting application code.

This independence from the underlying system call details is achieved by using a single style of event notification, in this case notification of operation completion.

To obtain a completion notification, the application code registers completion functions with the connection<>, connector<> and acceptor<>. There are several choices for doing this - you can use a class that models a concept, a class that implements a virtual base interface, or individual boost::function notifications.

Simple Finger Service

There is a Twisted tutorial on building a finger service, so I thought it would be nice to see how this shaped up when implemented with Giallo.

Refuse Connections

#include <boost/net/demultiplexer.hpp>
#include <boost/net/demultiplexer/impl/reactor_select_dispatcher.hpp>

int main(int, char**)
{
   boost::net::demultiplexer<boost::net::demux::reactor_select_dispatcher> demux;
   demux.run();
   return 0;
}

This example runs a select reactor, which having no objects to wait on, will return immediatly. The select based reactor is one of Giallo's demultiplexers. You can use as many different demultiplexers as you require. The actual demultiplexers available depend on both the platform that you are using and the type of objects that you are adding. Some of the demultiplexers are portable.

Do Nothing

To do something useful, we need to run as a server, which implies accepting incoming connections. We therefore add an acceptor.

#include <boost/net/demultiplexer.hpp>
#include <boost/net/demultiplexer/impl/reactor_select_dispatcher.hpp>
#include <boost/net/socket/acceptor.hpp>
#include <boost/net/socket/endpoint.hpp>
#include "boost/net/socket/ip4/address.hpp"
#include "boost/net/socket/ip4/protocol.hpp"

int main(int, char**)
{
   boost::net::demultiplexer<> demux;
   boost::net::socket::acceptor<> acceptor(demux);

   acceptor.open(
    boost::net::socket::make_endpoint(
      boost::net::socket::ip4::tcp_protocol(),
oost::net::socket::ip4::address("127.0.0.1", 1079) ) );

   demux.run();
   return 0;
}

This starts an acceptor listening on port 1079 (finger normally runs on the reserverd port 79, the standard port for finger servers). Connections to 1079 will be accepted, but closed automatically on any input.

at this point the socket has called listen, but not accept.

Drop Connections

To respond to an accepted connection we need to add an accept handler.

#include <boost/net/demultiplexer.hpp>
#include <boost/net/demultiplexer/impl/reactor_select_dispatcher.hpp>
#include <boost/net/socket/acceptor.hpp>
#include <boost/net/socket/endpoint.hpp>
#include "boost/net/socket/ip4/address.hpp"
#include "boost/net/socket/ip4/protocol.hpp"
#include <boost/function.hpp>

void on_accepted(boost::net::socket::socket& s,
                 boost::net::socket::any_address&)
{
  s.close();
}

int main(int, char**)
{
   boost::net::demultiplexer<> demux;
   boost::net::socket::acceptor<> acceptor(demux);
   acceptor.attach(boost::function(&on_accepted));

   acceptor.open(
    boost::net::socket::make_endpoint(
      boost::net::socket::ip4::tcp_protocol(),
oost::net::socket::ip4::address("127.0.0.1", 1079) ) );

   acceptor.accept();

   demux.run();
   return 0;
}

Here we respond to the event of beginning a connection by terminating it. To progress further it is going to be easiest to convert the above to something a little more object oriented. We do this by making the acceptor a memeber of a server class. Note that we have added an accept erro handler, and we now attach the class rather than just a single function.

#include <boost/net/demultiplexer.hpp>
#include <boost/net/demultiplexer/impl/reactor_select_dispatcher.hpp>
#include <boost/net/socket/acceptor.hpp>
#include <boost/net/socket/endpoint.hpp>
#include "boost/net/socket/ip4/address.hpp"
#include "boost/net/socket/ip4/protocol.hpp"
#include <boost/function.hpp>

class finger_server
{
public:
  finger_server(boost::net::demultiplexer<>& demux)
    : m_acceptor(demux)
  {
    m_acceptor.attach(this);

    m_acceptor.open(
      boost::net::socket::make_endpoint(
        boost::net::socket::ip4::tcp_protocol(),
 boost::net::socket::ip4::address("127.0.0.1", 1079) ) );

    m_acceptor.accept();
  }

  void on_accepted(boost::net::socket::socket& s,
                   boost::net::socket::any_address&)
  {
    s.close();
    m_acceptor.accept();
  }

  void on_acceptor_error(boost::net::acceptor_error err)
  {
  }

private:
  boost::net::socket::acceptor<> m_acceptor;
};

int main(int, char**)
{
   boost::net::demultiplexer<> demux;
   finger_server server(demux);
   demux.run();
   return 0;
}

Read Username, Drop Connections

No we add a connection class that does something with the accepted connection.

#include <boost/net/demultiplexer.hpp>
#include <boost/net/demultiplexer/impl/reactor_select_dispatcher.hpp>
#include <boost/net/socket/acceptor.hpp>
#include <boost/net/socket/connection.hpp>
#include <boost/net/socket/endpoint.hpp>
#include "boost/net/socket/ip4/address.hpp"
#include "boost/net/socket/ip4/protocol.hpp"
#include <boost/function.hpp>

class finger_connection
{
public:
  finger_connection(boost::net::socket::socket& s,
                    boost::net::demultiplexer<>& demux)
    :  m_connection(s, demux)
  {
    m_connection.attach(this);
    m_connection.on_close_notification(
      boost::bind(&finger_connection::on_finalise,this) );
    m_connection.recv(m_buffer,100);
  }


  void on_received(int n, int /*err*/)
  {
    m_buffer[n]=0;
    std::cerr << "finger: recevied request for " << m_buffer << std::endl;
    m_connection.close();
  }

  void on_sent(int n, int /*err*/){}
  void on_closed() {}
  void on_connection_error(boost::net::connection_error){}

  void on_finalise()
  {
    delete this;
  }

private:
  char m_buffer[100];
  boost::net::socket::connection<> m_connection;
};

class finger_server
{
public:
  finger_server(boost::net::demultiplexer<>& demux)
    : m_acceptor(demux),
      m_demultiplexer(demux)
  {
    m_acceptor.attach(this);

    m_acceptor.open(
      boost::net::socket::make_endpoint(
        boost::net::socket::ip4::tcp_protocol(),
 boost::net::socket::ip4::address("127.0.0.1", 1079) ) );

    m_acceptor.accept();
  }

  void on_accepted(boost::net::socket::socket& s,
                   boost::net::socket::any_address&)
  {
    new finger_connection(s,m_demultiplexer);
    m_acceptor.accept();
  }

  void on_acceptor_error(boost::net::acceptor_error err)
  {
  }

private:
  boost::net::socket::acceptor<> m_acceptor;
  boost::net::demultiplexer<>& m_demultiplexer;
};

int main(int, char**)
{
   boost::net::demultiplexer<> demux;
   finger_server server(demux);
   demux.run();
   return 0;
}
Copyright 2004 Hugo Duncan

PrevUpHomeNext