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.
|
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
|