MySVC
an open source UNIX framework for shell script based web services provider
»Home
»Tools
»Man Page
»User Guide
»ChangeLog
»Installation
»Source Code
»Downloads
»FAQ
»Support
»License

»My Apps

Table of Contents

Introduction

MySPL (My Service Protocol Library) is a C++ library to implement any TCP/IP socket client/server for binary or ASCII protocols.

It’s made from two source code files:

For example, to implement a socket client for a binary protocol you must: - use the library classes defining your protocol messages from their fields, where messages can be: * flat (a sequence of fields each of its type and size) * a tree hierarchy - using them: * creating a SocketClient from ip address and port * instantiating the message objects * implementing the workflow corresponding to the protocol reading and writing messages simply using the operators << and >>, or accessing their fields by name or by index

The same to implement a socket client for an ASCII protocol but using false for parameter binary to SocketClient constructor

To implement a socket server you must use the SocketServer class

Examples

...
//
// Socket client for a binary protocol
//

// define message classes...

//
// VERSION
//

class UPSPVersion: public Message
{
  public:
    UPSPVersion():
      Message(5,"VERSION")
    {
      // Header fields
      assign(0,new Int4Field("length",18));
      assign(1,new Int2Field("msg_type",UPSP_VERSION));
      assign(2,new Int4Field("client_data_1"));
      assign(3,new Int4Field("client_data_2"));

      // Body field
      assign(4,new Int4Field("version",4));
    }
};

//
// LOGIN_REQ
//

class UPSPLoginReq: public Message
{
  public:
    UPSPLoginReq():
      Message(6,"LOGIN_REQ")
    {
      // Header fields
      assign(0,new Int4Field("length",71));
      assign(1,new Int2Field("msg_type",UPSP_LOGIN_REQ));
      assign(2,new Int4Field("client_data_1"));
      assign(3,new Int4Field("client_data_2"));

      // Body fields
      assign(4,new StringField(32,"username"));  // to init
      assign(5,new StringField(25,"password"));  // to init
    }
};

//
// LOGIN_ACK
//

class UPSPLoginAck: public Message
{
  public:
    UPSPLoginAck():
      Message(4,"LOGIN_ACK")
    {
      // Header fields
      assign(0,new Int4Field("length",14));
      assign(1,new Int2Field("msg_type",UPSP_LOGIN_ACK));
      assign(2,new Int4Field("client_data_1"));
      assign(3,new Int4Field("client_data_2"));
    }
};

//
// LOGIN_NACK
//

class UPSPLoginNAck: public Message
{
  public:
    UPSPLoginNAck():
      Message(6,"LOGIN_NACK")
    {
      // Header fields
      assign(0,new Int4Field("length",530));
      assign(1,new Int2Field("msg_type",UPSP_LOGIN_NACK));
      assign(2,new Int4Field("client_data_1"));
      assign(3,new Int4Field("client_data_2"));

      // Body fields
      assign(4,new Int4Field("error_code"));
      assign(5,new StringField(512,"error_msg"));
    }
};
...

//
// create a SocketClient from ip address and port
// and use message classes to implement the
// protocol workflow reading and writing messages
// simply using the operators >> and <<
// or accessing their fields by name or by index...
//

  ...
  try
  {
    SocketClient wildfire(ipaddress,port);

    // Connection to wildfire ...

    if(upsp_log)
      cerr << "Connecting to Wildfire ..." << endl;

    wildfire.connect();

    UPSPHeader               header;

    UPSPVersion              version;
    UPSPLoginReq             login_req;
    ...
    int msg_type;
    ...

    // Login ...

    if(upsp_log)
      cerr << "Sending version ..." << endl;

    version[4] = "1.0";

    if(upsp_log)
    {
      if(upsp_trace)
        cerr << version << endl;
      cerr << "  <version> = <"
           << ((Int4Field&) version[4]).value()
           << ">" << endl;
    }

    wildfire << version;

    login_req[4] = "myusername";
    login_req[5] = "mypassword";

    if(upsp_log)
    {
      cerr << "Sending login_req ..." << endl;
      if(upsp_trace)
        cerr << login_req;
      cerr << "  <username> = <" << username << ">" << endl;
      cerr << "  <password> = <" << password << ">" << endl;
    }

    wildfire << login_req;

    if(upsp_log)
      cerr << "Reading ..." << endl;

    wildfire >> header;

    if(upsp_log)
    {
      if(upsp_trace)
        cerr << header << endl;
    }

    msg_type = (Int2Field&) header[1];

    switch(msg_type)
    {
      case UPSP_LOGIN_ACK:
      {
        if(upsp_log)
          cerr << "Received login_ack ..." << endl;

        break;
      }
      case UPSP_LOGIN_NACK:
      {
        if(upsp_log)
        {
          cerr << "Received login_nack ..." << endl;
          cerr << "Reading login_nack body ..." << endl;
        }

        wildfire >> login_nack_body;

        if(upsp_log)
        {
          if(upsp_trace)
            cerr << login_nack_body << endl;
          cerr << "  <error_code> = <"
               << ((Int4Field&) login_nack_body[0]).value()
               << ">" << endl;
          cerr << "  <error_msg> = <"
               << ((StringField&) login_nack_body[1]).value()
               << ">" << endl;
        }

        break;
      }
      ...
   }
   ...
  }
  catch(SocketException e)
  {
    cerr << e << endl;

    return WF_CONNECTION_ERROR;
  }
  ...

//
// Socket client for an ASCII protocol
//
  ...
  try
  {
    // Connect to server (ascii mode) ...

    SocketClient client(addr,port,false);

    client.connect();

    StringField request;
    StringField response(APT_MAXLINESIZE);

    string requeststr;
    string responsestr;

    // input is a sequence of <request_block>;
    // each <request_block> has the following syntax:
    //
    // <request_line>
    // <request_line>
    // ...
    // <request_line>
    // %<substring_of_last_response_line>%
    //
    // Send request lines of input stream to server
    // and write response lines from server to output stream ...

    while(getline(*inputstream,requeststr))
    {
      if ((requeststr.length() >= 2) &&
          (requeststr[0] == '%') &&
          (requeststr[requeststr.length()-1] == '%'))
      {
        string last;

        if(requeststr.length() > 2)
          last = requeststr.substr(1,requeststr.length()-2);

        while(1)
        {
          try
          {
            // Reading response ...
            client >> response;

            responsestr = response.value();
          }
          catch(...)
          {
            cerr << APT_RECV_MSGERROR << endl;

            try
            {
              client.close();
            }
            catch(...)
            {
            }

            return APT_RECV_ERROR;
          }

          (*outputstream) << responsestr << endl;

          if(responsestr.find(last) != string::npos)
            break;
        }

        continue;
      }

      request = requeststr;

      try
      {
        // Sending request ...
        client << request;
      }
      catch(...)
      {
        cerr << APT_SEND_MSGERROR << endl;

        try
        {
          client.close();
        }
        catch(...)
        {
        }

        return APT_SEND_ERROR;
      }
    }

    client.close();
  }
  catch(SocketException e)
  {
    cerr << e << endl;

    return APT_CONNECTION_ERROR;
  }
  ...

//
// Socket client for a binary protocol with tree messages
//

// define fields and messages...

...
class SMIShortField: public Int2Field
{
  public:
    SMIShortField(short value = 0,string name = "SMIShort"):
      Int2Field(name,value)
    {
    }
};

class SMIShort: public FieldMessage
{
  public:
     SMIShort(short value = 0,string name = "SMIShort"):
       FieldMessage(new SMIShortField(value,name),name)
     {
     }

     short value()
     {
       return ((SMIShortField&)(*this)[0]).value();
     }

     operator short()
     {
       return value();
     }

     SMIShort& operator=(short value)
     {
       (Int2Field&)(*this)[0] = value;
       return *this;
     }
};
...
class SMIOctetField: public OctetField
{
  public:
    SMIOctetField(char value = '\0',string name = "SMIOctet"):
      OctetField(name,value)
    {
    }
};
...
class SMIString: public TreeMessage
{
  public:
    SMIString(string value = "",string name = "SMIString"):
      TreeMessage(1+value.length(),name)
    {
       (*this)(0) = new TreeMessage(
         new SMIULong(size()-1,"SMIString length"));

       for(int i=1;i<size();i++)
         (*this)(i) = new TreeMessage(
           new SMIOctet(value[i-1]));
    }

    string value()
    {
      string s;

      for(int i=1;i<size();i++)
        s += ((SMIOctet&)(*this)[i]()).value();

      return s;
    }

     operator string()
     {
       return value();
     }

     SMIString& operator=(string value)
     {
       init(1+value.length());

       (*this)(0) = new TreeMessage(
         new SMIULong(size()-1,"SMIString length"));

       for(int i=1;i<size();i++)
         (*this)(i) = new TreeMessage(
           new SMIOctet(value[i-1]));

       return *this;
     }

  protected:
    TreeMessage* child(int i)
    {
      return new TreeMessage(
        new SMIOctet());
    }

    int update(int size_)
    {
       int length = ((SMIULong*)(*this)[0].info())->value();

       if((size_ == 1) && (length > 0))
         return 1 + length;
       else
         return 0;
    }
};
...

// use messages to implement the protocol workflow...

  try
  {
    SocketClient client(scp_addr,scp_port);

    if(scp_log)
      cerr << "Connecting to SCP ..." << endl;

    try
    {
      client.connect();
    }
    catch(Exception& e)
    {
      if(scp_log)
        cerr << "Error connecting to SCP ..." << endl;

      return SCP_CONNECTION_ERROR;
    }

    // Login

    SMILogin login(scp_username,scp_password);

    if(scp_log)
      cerr << "Sending login message ..." << endl;

    if(scp_trace)
    {
      login.updateTree();

      cerr << "<smi>" << endl;

      cerr << login << endl;
    }

    client << login;

    SMILoginResp loginresp;

    client >> loginresp;
    ...
  }
  catch(Exception& e)
  {
     cerr << e << endl;

     return 1;
  }
  ...

To use myspl library (libmyspl.a):

  • include protocol.hh into your C++ source code

  • link the library to your C++ main application with a command like the following

g++ -lmyspl myapp.cc

or

g++ -L<myspldir> -lmyspl myapp.cc

to specify the dir containing <myspldir>, if different from current directory

For details see entire the MySPL example clients source code:

  • Wildfire VMS Client, realized for provisioning of Memory service in blu mobile telephone operator:

  • Nortel Networks IN (Intelligent Network) SMI (Service Management Interface) Client, realized for provisioning of VPN service on SCP in blu mobile telephone operator:

  • Simple tester application for ASCII protocols, realized to test many provisioning protocols on Network Elements in blu mobile telephone operator: