Added UDP Transmitter to DKit to Round Out I/O Package

DKit Laboratory

This morning I wrote a simple, yet effective, UDP transmitter for DKit. The idea is simple - if we have a UDP receiver for datagrams, then it makes sense to have a transmitter of the same. Simple make it a subclass of the sink and we should be ready to go. We can have it share io_service threads like the receivers, but since the transmitters have a slightly different usage on the io_service threads, it's probably a good idea to leave them separate for now.

We can certainly make a simple io_service base class for both the receiver and transmitter and pull these into this base class if we need to, but since the transmitter's need to have a boost::io_service::work defined for them - lest the io_service's run() method return too quickly due to nothing to do, it's different than the receivers, and simple enough to pool on the transmitters, as needed.

The one trick was the virtual template classes problem that I solved previously. I do not want to make the inclusion of the UDP transmitter require that the user know what type to use, so I made a very simple, and very clean subclass that then becomes the basis of the UDP transmitter:

  namespace detail {
  template <class T> class basic_udp_transmitter :
    public sink<T>
  {
    public:
      basic_udp_transmitter() { }
      virtual ~basic_udp_transmitter() { }
 
      /**
       * This is the main receiver method that we need to call out to
       * a concrete method for the type we're using. It's what we have
       * to do to really get a virtual template class working for us.
       */
      virtual bool recv( const T anItem )
      {
        return onMessage(anItem);
      }
 
      /**
       * This method is called when we get a new datagram, and because
       * we are expecting to instantiate this template class with the
       * type 'T' being a <datagram *>, this is the method we're expecting
       * to get hit. It's just that simple.
       */
      virtual bool onMessage( const datagram *dg )
      {
        /**
         * This method will be overridden by the specialization of
         * this template class.
         */
        return true;
      }
  };

and then the main class looks like this:

  class udp_transmitter :
    public detail::basic_udp_transmitter<datagram*>
  {
 
    // …
 
    /********************************************************
     *
     *                Processing Methods
     *
     ********************************************************/
    /**
     * This is the specialized template method that will be called from
     * the generalized recv() method, above, and will, in fact, be the
     * place where we do all the work of sending out the datagram to the
     * UDP multicast channel.
     */
    virtual bool onMessage( const datagram *aDatagram );
 
    // …
 
  };

At this point, it's easy to use the udp_transmitter because I've wired it all up to send the datagrams out via boost ASIO:

  udp_transmitter  xmit(multicast_channel("udp://239.255.1.1:30001"));
  rcvr.addToListeners(&xmit);

The threads are all handled in the same manner as the UDP receiver, so we know they are clean with the reference counting and the clearing of the sockets. It's a nice, clean implementation. You just need to wire it up to a source and it's going to send out those datagrams as they arrive.

Sweet.

It's nice to get this done and really get the next cut of the library done. It's now got receivers and transmitters, and I can build on these as needed or add in more receivers and transmitters (ZeroMQ, etc.) as needed.