Building XML-RPC Clients in C
by Joe Johnston
|
Pages: 1, 2, 3, 4
Listing 11 demonstrates how to deal with
structs. Much like arrays, you can iterate over key-value pairs in
structs. The function xmlrpc_struct_size returns the
number of key-value pairs available. A call to
xmlrpc_get_key_and_value will point the two passed-in
pointers to xmlrpc_value pointers at the n-th key-value
pair. Although keys are almost always strings, I err on the side of
caution here by asking print_values to display the key's
value.
Listing 11: xmlrpc_debug.c, part 10
188 void get_struct(xmlrpc_env* env, xmlrpc_value* in){
189 int i, size = 0;
190 xmlrpc_value *key, *value;
191
192 size = xmlrpc_struct_size(env, in);
193 die_if_fault_occurred(env);
194
195 for(i=0; i < size; i++){
196 xmlrpc_struct_get_key_and_value(env,
197 in,
198 i,
199 &key,
200 &value);
201 die_if_fault_occurred(env);
202
203 printf("\tkey: ");
204 print_values(env, key);
205 printf("\tvalue: ");
206 print_values(env, value);
207 }
208 }
209
|
The program concludes with Listing 12. This is a routine that conveniently catches errors. It was also ripped in whole from the example code in the XML-RPC C library.
Listing 12: xmlrpc_debug.c, part 11
210 void die_if_fault_occurred (xmlrpc_env *env){
211
212 if (env->fault_occurred) {
213 fprintf(stderr, "XML-RPC Fault: %s (%d)\n",
214 env->fault_string, env->fault_code);
215 exit(1);
216 }
217 }
|
The Makefile
|
Binary Data to
Go: Using XML-RPC to Serve Up Charts on the Fly |
Once the source code is ready, you'll want to compile it. There are a lot of shared libraries that need to be linked into any XML-RPC client program. Eric Kidd's documentation suggests that you initialize your variables like this:
$ CLIENT_CFLAGS=`xmlrpc-c-config libwww-client --cflags` $ CLIENT_LIBS=`xmlrpc-c-config libwww-client --libs`
I have simply expanded these so that I can track down missing library errors more easily. Listing 13 shows the barebones Makefile used to compile this debug client. All warnings are turned on and it should compile without warnings under gcc.
1 CLIENT_CFLAGS=-I/usr/include
2 CLIENT_LIBS=-L/usr/lib -lxmlrpc_client \
3 -lwwwxml -lxmltok -lxmlparse -lwwwzip \
4 -lwwwinit -lwwwapp -lmd5 -lwwwhtml -lwwwtelnet \
5 -lwwwnews -lwwwhttp -lwwwmime -lwwwgopher -lwwwftp \
6 -lwwwfile -lwwwdir -lwwwcache -lwwwstream -lwwwmux \
7 -lwwwtrans -lwwwcore -lwwwutils -ldl -lz \
8 -lxmlrpc -lxmlrpc_xmlparse -lxmlrpc_xmltok -Wl,--rpath -Wl,/usr/lib
9
10 CFLAGS=-Wall -ansi -pedantic -g
11
12 debug:
13 gcc $(CFLAGS) $(CLIENT_CFLAGS) -o \
xmlrpc_debug xmlrpc_debug.c $(CLIENT_LIBS)
|
A Simple Perl Server
The test harness for the debug client is a simple Perl XML-RPC server. It returns sample data of various formats. Like all Frontier::Daemon programs, it runs as a single-threaded HTTP server, listening, in this case, to TCP port 3080. The remote procedures this server handles are given in the methods hash, which is passed to Frontier::Daemon during initialization. Because these functions are so simple, anonymous subroutines are used instead of creating named subroutines and passing references to them. Normally, the Frontier::RPC2 library is good about guessing how to encode Perl scalars, but base64 strings need manual intervention. Line 26 in Listing 14 below shows this in action.
1 #!/usr/bin/perl --
2 # just return sample data
3
4 use strict;
5 use Frontier::Daemon;
6 use MIME::Base64;
7
8 use constant PORT => 3080;
9 use constant SERVER_URL => 'http://localhost:'.PORT.'/RPC2';
10
11 print "Starting: ", SERVER_URL, "\n";
12 my $coder = Frontier::RPC2->new;
13 Frontier::Daemon->new(
14 methods => {
15 str => sub{ return "AB "x512 },
16 int => sub{ return 12345 },
17 double => sub{ return 1.234 },
18 array => sub{ return [(0..128)] },
19 struct => sub{ return {
20 key1 => 1,
21 key2 => "value2",
22 key3 => 4.56,
23 }
24 },
25 base64 => sub{ return
26 $coder->base64(
27 encode_base64('I love Lucy')
28 )
29 },
30
31 mixed => sub {
32 return [
33 { key1 => 1.0 },
34 { key2 => 2.0 },
35 { key3 => 'three'},
36 ];
37 },
38 },
39
40 LocalPort => PORT,
41 Reuse => 1,
42 );
|
What's Next?
This code should give the aspiring C XML-RPC programmer a good starting point for building a real application. It's hard to beat a C program for execution speed. A program similar to the debug client shown here, but written in Perl or Python, will run five to ten times slower. Of course, the Perl or Python script is five to ten times faster to write. You can save either CPU time or programmer time, but not both. Happy hacking.
Joe Johnston is an independent contractor and freelance writer. A graduate of the University of Massachusetts in Boston with a B.A. in computer science, he is a teacher, a Web designer, and an author of articles for the Perl Journal and other publications. Joe coauthored O'Reilly's Programming Web Services with XML_RPC. He can be emailed at jjohn@cs.umb.edu.
O'Reilly & Associates recently released (June 2001) Programming Web Services with XML-RPC.
Sample Chapter 3, Client-Server Communication: XML-RPC in Java, is available free online.
You can also look at the Table of Contents, the Index, and the Full Description of the book.
For more information, or to order the book, click here.