Listing 4: Relay loop with a finite state machine


class PacketRelay
{
  StreamSocket& sock1;
  StreamSocket& sock2;

  char sock1_name[80];                  // Socket names for easy reference
  char sock2_name[80];                  // (name cache)

  char * buffer;
  enum { IO_buffer_size=12000 };

  BOOL done;

  static PacketRelay * the_PacketRelay;

  void intr_enable(void);
  void intr_disable(void);
                                // Finite State Machine "Actors"
  enum StatusCode {OK=0, NoData, Eof, Failed};  // what Actors return
  enum SOCK_ID { id_sock1, id_sock2 };
  typedef StatusCode (*Actor)(PacketRelay * _this, const SOCK_ID id);

                                        // Meaning write to another socket  and
                                        // keep the log
  StatusCode relay_socket(const SOCK_ID id);
  BOOL done_loop;
  StatusCode is_loop_done(const SOCK_ID id)    { return done_loop ? Eof : OK; }
  StatusCode set_loop_done(const SOCK_ID id)   { done_loop = TRUE; return OK; }
  StatusCode reset_loop_done(const SOCK_ID id) { done_loop = FALSE; return OK;}
  StatusCode set_finished(const SOCK_ID id)    { done = TRUE; return OK;}
                                // Relay Finite State Machine
  struct FSMAction { Actor   actor;
                     SOCK_ID socket_to_use;
                     short   next_state[Eof+1]; };
  static FSMAction FSMProgram [];

public:
  PacketRelay(StreamSocket& _sock1, StreamSocket& _sock2);
  ~PacketRelay(void);
  BOOL q_done(void) const       { return done; }
  static int relay(void);               // That's what relays (if anything)
};

PacketRelay * PacketRelay::the_PacketRelay = 0;

                                // Here is the FSM relay program
PacketRelay::FSMAction PacketRelay::FSMProgram [] =
{
 /* 0 */ {0,    id_sock1,       {0,0,0}},       // Dummy (stop) state

//  State       What to do     on which socket   OK ND EOF
 /* 1 */ { (Actor)&relay_socket,  id_sock1,     {2,  3,  5} },  // start state
 /* 2 */ { (Actor)&relay_socket,  id_sock2,     {1,  4,  6} },
 /* 3 */ { (Actor)&set_loop_done, id_sock1,     {2, -1, -1} },
 /* 4 */ { (Actor)&is_loop_done,  id_sock1,     {1,  0,  0} },
 /* 5 */ { (Actor)&relay_socket,  id_sock2,     {5,  7,  7} },
 /* 6 */ { (Actor)&relay_socket,  id_sock1,     {6,  7,  7} },
 /* 7 */ { (Actor)&set_finished,  id_sock1,     {0,  0,  0} }
};

                                    // This is where it relays: the Finite
                                    // State Machine
                                    // Start with the state #1, do the action and
                                    // pick up the next state based on the action's
                                    // return code. Keep doing it until hit the
                                    // stop state (pc=0)
int PacketRelay::relay(void)
{
  PacketRelay& me = *the_PacketRelay;
  if( me.done )
    return 0;

  int pc;                           // "Program" counter
  for(pc=1; pc != 0; )
  {
    assert( pc > 0 );
    const FSMAction& action = FSMProgram[pc];
    StatusCode result = action.actor(&me,action.socket_to_use);
    if( result == Failed )
      return me.done = TRUE, 0;
    pc = action.next_state[result];
  }
  return 0;
}

//End of File