XML.com: XML From the Inside Out
oreilly.comSafari Bookshelf.Conferences.

advertisement

Building XML-RPC Clients in C
by Joe Johnston | Pages: 1, 2, 3, 4

The Debug Client

When learning a new XML-RPC implementation, the most important thing to learn is how it deals with the XML-RPC datatypes listed in Table 1.

Table 1: XML-RPC Datatypes

TypeDescription
stringASCII string
int32-bit signed integer
doubledouble-precision floating point (exact range is implementation dependent)
booleanvalues can be 0 or 1
dateTime.iso8601An ASCII string that is date/time stamp. Time-zone information must be negotiated separately
base64An ASCII string which is base64 encoded
arrayAn order sequence of any valid XML-RPC datatype
structAn unordered set of key-value pairs. Keys are ASCII strings. Values can be any XML-RPC datatype.

Recall that XML-RPC libraries map language-specific datatypes to XML-RPC datatypes. It makes sense, then, to build a client that has to deal with all possible XML-RPC datatypes. This program is normally invoked like this:

 $ ./xmlrpc_debug http://somewhere.com/RPC2 get_array

The first argument is a URL to an XML-RPC server. The second argument is the name of a remote procedure that doesn't require input. The code for this client begins in Listing 2. As is typical of most C programs, various header files are pulled in.

Listing 2: xmlrpc_debug.c, part 1

     1  #include <stdio.h>
     2  #include <stdlib.h>
     3  
     4  
     5  #include <string.h>
     6  #include <xmlrpc.h>
     7  #include <xmlrpc_client.h>
     8  

Listing 3 defines some constants and function prototypes. As a point of style, I try to make default values constants. This makes them easy to change, since most #define statements are listed at the beginning of code.

Listing 3: xmlrpc_debug.c, part 2


     9  #define NAME        "DEBUG XML-RPC C Client"
    10  #define VERSION     "0.1"
    11  #define SERVER_URL  "http://127.0.0.1:3080/RPC2"
    12  #define DEFAULT_RPC "status"
    13  
    14  void   die_if_fault_occurred(xmlrpc_env*);
    15  void   print_values(xmlrpc_env*, xmlrpc_value*);
    16  int    get_int(xmlrpc_env*, xmlrpc_value*);
    17  int    get_boolean(xmlrpc_env*, xmlrpc_value*);
    18  double get_double(xmlrpc_env*, xmlrpc_value*);
    19  char*  get_timestamp(xmlrpc_env*, xmlrpc_value*);
    20  char*  get_string(xmlrpc_env*, xmlrpc_value*);
    21  char*  get_base64(xmlrpc_env*, xmlrpc_value*);
    22  void   get_array(xmlrpc_env*, xmlrpc_value*);
    23  void   get_struct(xmlrpc_env*, xmlrpc_value*);
    24

The main line begins in Listing 4. Line 26 declares an important variable that is used by nearly every XML-RPC function. The xmlrpc_env variable env will contain error messages and other transactional information. You don't need to worry about manipulating it directly, though. It's a kind of black box.


Visit xml.oreilly.com for a complete list of O'Reilly's books on XML.


Line 32 initializes some internal flags, and line 33 sets up the xmlrpc_env variable. These lines are mostly a stock piece; you won't need to change them much for your own code.

Listing 4: xmlrpc_debug.c, part 3


    25  int main (int argc, char** argv){
    26      xmlrpc_env   env;
    27      xmlrpc_value *result;
    28      char* url;
    29      char* rpc;
    30  
    31      /* Start up our XML-RPC client library. */
    32      xmlrpc_client_init(XMLRPC_CLIENT_NO_FLAGS, 
                              NAME, VERSION);
    33      xmlrpc_env_init(&env);
    34  

Listing 4 examines the command line for parameters. If none are detected, default values are used. Again, if you are used to scripting languages, you might be surprised by the rather lengthy measures needed to deal with strings in C.

Listing 5: xmlrpc_debug.c, part 4


    35      /* figure out URL */
    36      if(argc > 0){
    37        url = argv[1];
    38      }else{
    39        if( (url = (char *) malloc(sizeof(SERVER_URL))) ){
    40          strcpy(url, SERVER_URL);
    41        }else{
    42          printf("ERROR: Couldn't malloc\n");
    43          exit(1);
    44        }
    45      }
    46  
    47      if(argc > 1){
    48        rpc = argv[2];
    49      }else{
    50        if( (rpc = (char *) malloc(sizeof(DEFAULT_RPC))) ){
    51          strcpy(rpc, DEFAULT_RPC);
    52        }else{
    53          printf("ERROR: Couldn't malloc\n");
    54          exit(1);
    55        }
    56      }
    57  

In Listing 5 after 60 lines of code, I'm ready to make the remote procedure call. In your own code, you will be dealing with xmlrpc_client_call since it is the way to send your request to the server. This function's third argument is a kind of format string that tells the library how to map C data into XML-RPC data. Although the code in Listing 5 doesn't actually pass any data, here's a brief snippet that passes one string to a remote procedure:

result = xmlrpc_client_call( &env,
                             url,
                             "some_func",
                             "(s)",
                             "my great argument"
                           )

Here's a snippet that creates a struct:

result = xmlrpc_client_call( &env,
                             url,
                             "some_func",
                             "({s:i,s:i})",
                             "my great argument", 1,
                             "my next argument",  2
                           )

See the "overview.txt" document that's included with Kidd's XML-RPC library for more details. The meat of this program occurs on line 70 when print_values is invoked. Given the result of the remote procedure call, it simply decodes and prints out the values it received. I'll show the implementation of this function below.

Listing 6: xmlrpc_debug.c, part 5


    58      printf("Calling %s\n", url);
    59  
    60      /* Call our XML-RPC server. */
    61      result = xmlrpc_client_call(&env, 
    62                                  url,
    63                                  rpc,
    64                                  "()"
    65                                  );
    66  
    67      die_if_fault_occurred(&env);
    68  
    69      /* What did we get? */
    70      print_values(&env, result);
    71  

Pages: 1, 2, 3, 4

Next Pagearrow