郵便番号シリーズの最終回は、第一回目の SQLite を使って郵便番号から住所を検索するプログラムで作成したデータベースを利用して、 C言語用簡易XML解析ライブラリ:SXML を使った XML-RPC サーバ/クライアントのプログラムを実装します。
基本概念は第一回目の郵便番号検索と同じですが、 実装方法が XML-RPC というネットワーク通信を使った仕組みになります。 つまり、第一回目の講座で作成したプログラムは、 それ自身で問い合わせに対してデータベースを検索し、 結果を表示していました。 今回は、データベースの検索処理部分と郵便番号の問い合わせ/住所の表示処理部分が、 それぞれサーバとクライアントに分離したプログラムとなります。
7桁の郵便番号を送信すると、該当する住所を返す API を設計します。 また、郵便番号が大口事業所の場合は、事業所名も返すようにします。 メソッド名とパラメータは、次の通りです。
リクエストの実行に失敗した場合は、 以下のコード表に従ったエラーレスポンスを返します。
コード | メッセージ |
---|---|
400 | No method name |
401 | Invalid type of parameter |
402 | Request a string parameter as a 7-digit number |
403 | Unsupported method |
404 | Failed to access database |
405 | No address for postcode |
リクエストの実行に成功した場合は、<postcode> の値に検索した郵便番号を <address> の値に住所を設定して返します。 郵便番号が大口事業所の場合は、<office> の値に事業所名を設定して返します。
この API を用いたクライアントとサーバの通信内容は次のようになります。
<?xml version="1.0" encoding="us-ascii"?> <methodCall> <methodName>postal.getAddress</methodName> <params> <param><value><string>1008798</string></value></param> </params> </methodCall>
<?xml version="1.0" encoding="euc-jp"?> <methodResponse> <params> <param> <value> <struct> <member> <name>postcode</name> <value><string>1008798</string></value> </member> <member> <name>address</name> <value><string>東京都千代田区霞が関1丁目3−2</string></value> </member> <member> <name>office</name> <value><string>日本郵政公社</string></value> </member> </struct> </value> </param> </params> </methodResponse>
最初に、main() 関数の基本部分を考えます。 XML-RPC サーバを実装するには、sxmlrpc_new() 関数の引数として、 サーバの IPアドレス(ホスト名)とサービスを提供するポート番号、 そして URI 用のパスを渡します。 URI のパスに関しては、API 仕様で定義した /xmlrpc を指定します。
また、郵便番号データベースの文字コードは EUC-JP で作成したので、 sxmlrpc_set_encoding() 関数を使って "euc-jp" と設定します。 次に、sxmlrpc_server() 関数に、 リクエストを処理するためのサーバの待ち行列の大きさと、 実際にリクエストを処理する callback 関数を指定します。 ここでは待ち行列に「8」を、 callback 関数には postal 関数を指定します。
int main(int argc, char * argv[])
{
sxmlrpc_t * sxRPC;
/* http://localhost:10080/xmlrpc でアクセスする */
sxRPC = sxmlrpc_new("localhost", "10080", "/xmlrpc");
if (sxRPC != NULL) {
sxmlrpc_set_encoding(sxRPC, "euc-jp");
sxmlrpc_server(sxRPC, 8, postal);
sxmlrpc_free(sxRPC);
}
return 0;
}
今度は、実際にリクエストを処理する callback 関数を実装します。 基本的には、第一回目で実装した postal.c の postal() 関数と同じ機能を提供するだけです。 callback 関数は以下に示すように、接続元 IP アドレスと methodName、 parameters と レスポンスで返すパラメータ用の変数を引数に取ります。 ここでは、methodName と parameters の値は、 それぞれ API 仕様で定義した postal.getAddress と 7桁の郵便番号になります。 また、戻り値は、API 仕様で定義したエラーレスポンスのコードとなります。
static int
postal(const char * client, /* 接続元 IP アドレス */
const char * method, /* methodName */
sxmlrpc_params_t * params, /* parameters */
sxmlrpc_param_t * param) /* 値が設定されて戻される */
{
...
}
それでは、callback 関数の postal() を実装していきます。 最初に、XML-RPC プログラムのためのエラーチェックです。 sxmlrpc_set_fault() 関数を使って API 仕様で定義したエラーを設定します。
/* メソッドがない */
if (method == NULL) {
return sxmlrpc_set_fault(param, 400, "No method name");
}
/* メソッド名が違う */
if (strcmp(method, "postal.getAddress") != 0) {
return sxmlrpc_set_fault(param, 403, "Unsupported method");
}
/* パラメータがない/個数が違う */
if (params == NULL || params->size != 1) {
return sxmlrpc_set_fault(param, 402,
"Request a string parameter as a 7-digit number");
}
/* パラメータの値が文字列でない */
if (sxmlrpc_get_value_type(¶ms->param->value)
!= SXMLRPC_VALUE_STRING) {
return sxmlrpc_set_fault(param, 401, "Invalid type of parameter");
}
データベースをオープンするまでは、第一回目で作成したプログラム postal.c のコードと同じです。 それ以降は、第二回目で作成したプログラム postal.cgi.c のコードと同様の処理をしています。 なお、データベースを検索する関数 lookup() は、 postal.c の lookup() と完全に同一です。
/* 郵便番号を postcode に設定 */
postcode = sxmlrpc_get_value_string(¶ms->param->value);
/* postcode が 7桁かをチェックする */
len = strlen(postcode);
if (len != POSTCODE_LEN) {
goto error;
}
/* postcode が 0〜9 の数値のみで構成されているかチェックする */
pos = strspn(postcode, digits);
if (pos != POSTCODE_LEN) {
goto error;
}
status = sqlite3_open(dbfile, &conn);
if (status != SQLITE_OK) {
return sxmlrpc_set_fault(param, 404, "Failed to access database");
}
/* ここまでが postal.c と同様のコード */
/* ここからが postal.cgi.c と同じようなコード */
status = lookup(conn, SQL_JAPAN, postcode, &address, NULL);
if (status != 0) {
status = lookup(conn, SQL_OFFICE, postcode, &address, &office);
}
sqlite3_close(conn); /* ここでデータベースを閉じておく(重要) */
最後に、検索結果を定義した API の仕様に合わせて構築します。 XML-RPC プログラムの返答は、概ね <struct> 構造になると思うので、 以下のコードをちょっと改良するだけで自作プログラムなどに再利用できるでしょう。 これで、callback 関数の postal() のコードは完成です。
if (status == 0) {
sxmlrpc_member_t * mval; /* <member> 用 */
int n = 2; /* 検索結果の数 */
if (office != NULL) {
n++; /* 事業所名があるので検索結果の項目が増える */
}
mval = (sxmlrpc_member_t *)calloc(n, sizeof(sxmlrpc_member_t));
if (mval != NULL) {
sxmlrpc_struct_t tval; /* <struct> 用 */
sxmlrpc_value_t val; /* <value> 用 */
int i = 0;
/* <member> 要素を構築 */
mval[i].name = strdup("postcode");
sxmlrpc_set_value_string(&mval[i].value, postcode); i++;
mval[i].name = strdup("address");
sxmlrpc_set_value_string(&mval[i].value, address); i++;
/* 事業所名がある場合 */
if (office != NULL) {
mval[i].name = strdup("office");
sxmlrpc_set_value_string(&mval[i].value, office); i++;
free(office);
}
sxmlrpc_set_struct(&tval, n, mval); /* <struct> 構築 */
sxmlrpc_set_value_struct(&val, tval); /* <value> 構築 */
sxmlrpc_set_param(param, val); /* レスポンス用の値を設定 */
}
free(address);
return 0;
}
/* 郵便番号に該当する住所がなかった */
return sxmlrpc_set_fault(param, 405, "No address for postcode");
error:
return sxmlrpc_set_fault(param, 402,
"Request a string parameter as a 7-digit number");
}
ホスト名やポート番号などをオプションで指定できるようにした XML-RPC サーバのソースコード postal_xmlrpc_server.c は、 次の通りです。
HTML 用にコメントなどを一部削除しています。 また、実際のソースコードと若干違いますが動作に影響はありません。
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <errno.h>
#include <sqlite3.h>
#include <sxmlrpc.h>
#include "postal_xmlrpc.h"
/* Program and Copyright */
const char Program[] = "postal_xmlrpc_server";
const char Copyright[] =
"Copyright (c) 2007 Kouichi ABE (WALL), All rights reserved.";
/* Macros and structures definition */
/*
* Numeric release version identifier:
* MNNFFPPS: major minor fix patch status
* The status nibble has one of the values 0 for development,
* 1 to e for betas 1 to 14, and f for release.
* The patch level is exactly that.
*/
#define POSTAL_VERSION_NUMBER 0x10000000L
#define POSTAL_VERSION "Postal-XMLRPC-Server/1.0"
#define POSTAL_VERSION_TEXT POSTAL_VERSION " (2007/08/29)"
#define POSTAL_VERSION_TEXT_LONG \
"Postal-XMLRPC-Server 1.0, Wed, Aug 29 2007"
/*****************************************************************************/
#define CODE_NO_METHOD_NAME 400
#define CODE_INVALID_PARAM_TYPE 401
#define CODE_REQUEST_STRING_PARAM 402
#define CODE_UNSUPPORTED_METHOD 403
#define CODE_DATABASE_ERROR 404
#define CODE_NO_ADDRESS 405
#define MESG_NO_METHOD_NAME "No method name"
#define MESG_INVALID_PARAM_TYPE "Invalid type of parameter"
#define MESG_REQUEST_STRING_PARAM \
"Request a string parameter as a 7-digit number"
#define MESG_UNSUPPORTED_METHOD "Unsupported method"
#define MESG_DATABASE_ERROR "Failed to access database"
#define MESG_NO_ADDRESS "No address for postcode"
#define POSTCODE_DB "/usr/local/share/postal/postcode.db"
#define POSTCODE_LEN (7)
#define SQL_JAPAN \
"SELECT prefecture,city,address FROM japan"
" WHERE postcode=$POSTCODE;"
#define SQL_OFFICE \
"SELECT prefecture,city,address,name FROM office"
" WHERE postcode=$POSTCODE;"
/* Lobal functions declaration */
static void version(void);
static void usage(void);
static int postal(const char * client, const char * method,
sxmlrpc_params_t * params, sxmlrpc_param_t * param);
static int lookup(sqlite3 * conn, const char * sql, const char * postcode,
char ** address, char ** name);
/* Local variables declaration */
static const char * dbfile = POSTCODE_DB;
/* Lobal variable definition */
static void
version(void)
{
fprintf(stderr, "%s\n%s\n\n", POSTAL_VERSION_TEXT, Copyright);
exit(64);
}
static void
usage(void)
{
fprintf(stderr, "usage: %s [-d <database>][<ipaddr> [<port>]]\n", Program);
fprintf(stderr, "\n [ example ]\n");
fprintf(stderr, " %s 192.168.1.23 8080\n", Program);
fprintf(stderr, " %s -d postcode.db 192.168.1.23\n\n", Program);
exit(64);
}
static int
postal(client, method, params, param)
const char * client;
const char * method;
sxmlrpc_params_t * params;
sxmlrpc_param_t * param; /* returns */
{
static const char digits[] = "0123456789";
const char * postcode;
size_t len;
size_t pos;
sqlite3 * conn;
char * address;
char * office = NULL; /* office name */
int status = -1;
/* 1st error check */
if (method == NULL) {
return sxmlrpc_set_fault(param, CODE_NO_METHOD_NAME,
MESG_NO_METHOD_NAME);
}
if (strcmp(method, METHOD_NAME) != 0) {
return sxmlrpc_set_fault(param, CODE_UNSUPPORTED_METHOD,
MESG_UNSUPPORTED_METHOD);
}
if (params == NULL || params->size != 1) {
return sxmlrpc_set_fault(param, CODE_REQUEST_STRING_PARAM,
MESG_REQUEST_STRING_PARAM);
}
if (sxmlrpc_get_value_type(¶ms->param->value)
!= SXMLRPC_VALUE_STRING) {
return sxmlrpc_set_fault(param, CODE_INVALID_PARAM_TYPE,
MESG_INVALID_PARAM_TYPE);
}
/* set postcode */
postcode = sxmlrpc_get_value_string(¶ms->param->value);
/* 2nd error check */
len = strlen(postcode);
if (len != POSTCODE_LEN) {
goto error;
}
pos = strspn(postcode, digits);
if (pos != POSTCODE_LEN) {
goto error;
}
/* main process */
status = sqlite3_open(dbfile, &conn);
if (status != SQLITE_OK) {
return sxmlrpc_set_fault(param, CODE_DATABASE_ERROR,
MESG_DATABASE_ERROR);
}
status = lookup(conn, SQL_JAPAN, postcode, &address, NULL);
if (status != 0) {
status = lookup(conn, SQL_OFFICE, postcode, &address, &office);
}
sqlite3_close(conn);
if (status == 0) {
sxmlrpc_member_t * mval;
int n = 2;
if (office != NULL) {
n++;
}
mval = (sxmlrpc_member_t *)calloc(n, sizeof(sxmlrpc_member_t));
if (mval != NULL) {
sxmlrpc_struct_t tval;
sxmlrpc_value_t val;
int i = 0;
mval[i].name = strdup("postcode");
sxmlrpc_set_value_string(&mval[i].value, postcode); i++;
mval[i].name = strdup("address");
sxmlrpc_set_value_string(&mval[i].value, address); i++;
if (office != NULL) {
mval[i].name = strdup("office");
sxmlrpc_set_value_string(&mval[i].value, office); i++;
free(office);
}
sxmlrpc_set_struct(&tval, n, mval);
sxmlrpc_set_value_struct(&val, tval);
sxmlrpc_set_param(param, val);
}
free(address);
return 0;
}
return sxmlrpc_set_fault(param, CODE_NO_ADDRESS, MESG_NO_ADDRESS);
error:
return sxmlrpc_set_fault(param, CODE_REQUEST_STRING_PARAM,
MESG_REQUEST_STRING_PARAM);
}
static int
lookup(conn, sql, postcode, address, name)
sqlite3 * conn;
const char * sql;
const char * postcode;
char ** address;
char ** name;
{
sqlite3_stmt * res;
int status;
status = sqlite3_prepare(conn, sql, (int)strlen(sql), &res, NULL);
if (status != SQLITE_OK) {
return -1;
}
status = sqlite3_bind_text(res, 1, postcode, POSTCODE_LEN,
SQLITE_TRANSIENT);
if (status != SQLITE_OK) {
status = -1;
goto done;
}
switch (sqlite3_step(res)) {
case SQLITE_ROW: {
int n;
n = sqlite3_column_count(res);
if (n >= 3) {
asprintf(address, "%s%s%s",
sqlite3_column_text(res, 0),
sqlite3_column_text(res, 1),
sqlite3_column_text(res, 2));
if (*address != NULL) {
status = 0;
}
if (n == 4 && name != NULL) {
*name = strdup(sqlite3_column_text(res, 3));
}
}
}
break;
case SQLITE_DONE:
status = 1;
break;
default:
status = -1;
break;
}
done:
sqlite3_finalize(res);
return status;
}
int
main(argc, argv)
int argc;
char * argv[];
{
register int ch;
const char * hostname = HOSTNAME;
const char * servname = SERVNAME;
sxmlrpc_t * sxRPC;
/* parse options */
while ((ch = getopt(argc, argv, "d:hv?")) != -1) {
switch (ch) {
case 'd': dbfile = optarg; break;
case 'v': version();
case 'h':
default:
usage();
}
}
argc -= optind;
argv += optind;
switch (argc) {
case 2: servname = argv[1];
case 1: hostname = argv[0];
default: break;
}
sxRPC = sxmlrpc_new(hostname, servname, REQUEST_URI);
if (sxRPC != NULL) {
sxmlrpc_set_encoding(sxRPC, "euc-jp");
sxmlrpc_server(sxRPC, 8, postal);
sxmlrpc_free(sxRPC);
}
return 0;
}
コンパイルは、次のように行います。
% gcc -I/usr/local/include -L/usr/local/lib -o postal_xmlrpc_server posta_xmlrpc_server.c -lsqlite3 -lsxmlrpc -lsxml
最初に、main() 関数の基本部分を考えます。 XML-RPC クライアントを実装するには、sxmlrpc_new() 関数の引数として、 サーバの IPアドレス(ホスト名)とサービスを提供するポート番号、 そして URI 用のパスを渡します。 URI のパスに関しては、API 仕様で定義した /xmlrpc を指定します。 ここまで、サーバ・プログラムと同じです。 クライアント・プログラムでは、 サーバに問い合わせを送信する関数を実装するだけです。 ここでは、それが postal() 関数となります。
int main(int argc, char * argv[])
{
sxmlrpc_t * sxRPC;
int status = -1;
/* http://localhost:10080/xmlrpc でアクセスする */
sxRPC = sxmlrpc_new("localhost", "10080", "/xmlrpc");
if (sxRPC != NULL) {
sxmlrpc_value_t val;
sxmlrpc_set_value_string(&val, argv[0]); /* 郵便番号を val に設定 */
status = postal(sxRPC, &val); /* XML-RPC サーバに問い合わせ */
sxmlrpc_free(sxRPC);
}
return status;
}
XML-RPC サーバに API 仕様で定義した構造で問い合わせを送信する postal() 関数を実装します。関数の引数の一つは、問い合わせ用の郵便番号です。 sxmlrpc_call() 関数で実際にサーバに接続して要求処理を行います。
static int
postal(sxRPC, postcode)
sxmlrpc_t * sxRPC;
sxmlrpc_value_t * postcode; /* 7桁の郵便番号 */
{
int status;
sxmlrpc_param_t p;
sxmlrpc_set_param(&p, *postcode); /* パラメータに変換 */
/* API で定義した methodName でサーバにリクエスト送る */
status = sxmlrpc_call(sxRPC, "postal.getAddress", &p, 1);
if (status == 0) {
switch (sxRPC->method) {
case SXMLRPC_METHOD_RESPONSE: { /* 成功 */
sxmlrpc_value_t * v; /* レスポンス値の保存用 */
/* 検索結果を変数 v に設定 */
v = sxmlrpc_get_response_value(sxRPC);
if (sxmlrpc_get_value_type(v) == SXMLRPC_VALUE_STRUCT) {
int n;
n = sxmlrpc_get_struct_size(v);
if (n >= 2) {
sxmlrpc_member_t * mval; /* <member> 用 */
mval = sxmlrpc_get_struct_member(v); /* <member> の値を取得 */
fprintf(stdout, "%s: %s",
sxmlrpc_get_value_string(&mval[0].value), /* 郵便番号 */
sxmlrpc_get_value_string(&mval[1].value)); /* 住所 */
if (n == 3) { /* 大口事業所の郵便番号だった */
fprintf(stdout, " %s",
sxmlrpc_get_value_string(&mval[2].value)); /* 事業所名 */
}
fputs("\n", stdout);
}
}
}
break;
case SXMLRPC_METHOD_RESPONSE_FAULT: /* エラー */
fprintf(stdout, "fault: %d: %s\n",
sxmlrpc_get_fault_code(sxRPC), /* API 仕様で定義したコード */
sxmlrpc_get_fault_string(sxRPC)); /* API 仕様で定義したメッセージ */
break;
default:
fprintf(stdout, "error: %d\n", sxRPC->method);
break;
}
sxmlrpc_flush(sxRPC);
}
return status;
}
ホスト名やポート番号などをオプションで指定できるようにした XML-RPC クライアントのソースコード postal_xmlrpc_client.c は、 次の通りです。
HTML 用にコメントなどを一部削除しています。
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <sxmlrpc.h>
#include "postal_xmlrpc.h"
/* Program and Copyright */
const char Program[] = "postal_xmlrpc_client";
const char Copyright[] =
"Copyright (c) 2007 Kouichi ABE (WALL), All rights reserved.";
/* Macros and structures definition */
/*
* Numeric release version identifier:
* MNNFFPPS: major minor fix patch status
* The status nibble has one of the values 0 for development,
* 1 to e for betas 1 to 14, and f for release.
* The patch level is exactly that.
*/
#define POSTAL_VERSION_NUMBER 0x10000000L
#define POSTAL_VERSION "Postal-XMLRPC-Client/1.0"
#define POSTAL_VERSION_TEXT POSTAL_VERSION " (2007/08/29)"
#define POSTAL_VERSION_TEXT_LONG \
"Postal-XMLRPC-Client 1.0, Wed, Aug 29 2007"
/* Lobal functions declaration */
static void version(void);
static void usage(void);
static int postal(sxmlrpc_t * sxRPC, sxmlrpc_value_t * postcode);
/* Lobal variable definition */
static void
version(void)
{
fprintf(stderr, "%s\n%s\n\n", POSTAL_VERSION_TEXT, Copyright);
exit(64);
}
static void
usage(void)
{
fprintf(stderr, "usage: %s <postcode> [<ipaddr> [<port>]]\n", Program);
fprintf(stderr, "\n [ example ]\n");
fprintf(stderr, " %s 1008798\n", Program);
fprintf(stderr, " %s 1008798 192.168.1.23\n", Program);
fprintf(stderr, " %s 1008798 192.168.1.23 8080\n\n", Program);
exit(64);
}
static int
postal(sxRPC, postcode)
sxmlrpc_t * sxRPC;
sxmlrpc_value_t * postcode;
{
int status;
sxmlrpc_param_t p;
sxmlrpc_set_param(&p, *postcode);
status = sxmlrpc_call(sxRPC, METHOD_NAME, &p, 1);
if (status == 0) {
switch (sxRPC->method) {
case SXMLRPC_METHOD_RESPONSE: {
sxmlrpc_value_t * v;
v = sxmlrpc_get_response_value(sxRPC);
if (sxmlrpc_get_value_type(v) == SXMLRPC_VALUE_STRUCT) {
int n;
n = sxmlrpc_get_struct_size(v);
if (n >= 2) {
sxmlrpc_member_t * mval;
mval = sxmlrpc_get_struct_member(v);
fprintf(stdout, "%s: %s",
sxmlrpc_get_value_string(&mval[0].value),
sxmlrpc_get_value_string(&mval[1].value));
if (n == 3) {
fprintf(stdout, " %s",
sxmlrpc_get_value_string(&mval[2].value));
}
fputs("\n", stdout);
}
}
}
break;
case SXMLRPC_METHOD_RESPONSE_FAULT:
fprintf(stdout, "fault: %d: %s\n",
sxmlrpc_get_fault_code(sxRPC),
sxmlrpc_get_fault_string(sxRPC));
break;
default:
fprintf(stdout, "error: %d\n", sxRPC->method);
break;
}
sxmlrpc_flush(sxRPC);
}
return status;
}
int
main(argc, argv)
int argc;
char * argv[];
{
register int ch;
const char * hostname = HOSTNAME;
const char * servname = SERVNAME;
sxmlrpc_t * sxRPC;
int status = -1;
while ((ch = getopt(argc, argv, "hv?")) != -1) {
switch (ch) {
case 'v': version();
case 'h':
default:
usage();
}
}
argc -= optind;
argv += optind;
if (argc == 0) {
usage();
}
switch (argc) {
case 3: servname = argv[2];
case 2: hostname = argv[1];
default: break;
}
sxRPC = sxmlrpc_new(hostname, servname, REQUEST_URI);
if (sxRPC != NULL) {
sxmlrpc_value_t val;
sxmlrpc_set_value_string(&val, argv[0]);
status = postal(sxRPC, &val);
sxmlrpc_free(sxRPC);
}
return status;
}
サーバとクライアントの共通ヘッダ postal_xmlrpc.h は、次の通りです。
HTML 用にコメントなどを一部削除しています。
#ifndef _POSTAL_XMLRPC_H
#define _POSTAL_XMLRPC_H
/* Macros and structures definition */
#define METHOD_NAME "postal.getAddress" /* API 仕様で定義したメソッド */
#define HOSTNAME "localhost" /* デフォルトのホスト名 */
#define SERVNAME "10080" /* デフォルトのポート番号 */
#define REQUEST_URI "/xmlrpc" /* API 仕様で定義したパス */
#endif /* _POSTAL_XMLRPC_H */
コンパイルは、次のように行います。
% gcc -I/usr/local/include -L/usr/local/lib -o postal_xmlrpc_client posta_xmlrpc_client.c -lsxmlrpc -lsxml
では、プログラムの動作確認を行ってみましょう。 最初に、XML-RPC サーバを起動させます。
% ./postal_xmlrpc_server -d postcode.db
次に、XML-RPC サーバを実行したのと別の xterm などのターミナルソフトウェアから XML-RPC クライアントを実行します。 まずは、全国版の郵便番号検索です。
% ./postal_xmlrpc_client 1006001 1006001: 東京都千代田区霞が関霞が関ビル(1階)
次に、事業所版の郵便番号検索です。 全国版の出力と違い、事業所名も表示されます。
% ./postal_xmlrpc_client 1008798 1008798: 東京都千代田区霞が関1丁目3−2 日本郵政公社
指定した郵便番号に該当する住所がない場合やエラーが起きた場合は、 API 仕様で定義したエラーコードに応じたメッセージが表示されます。 次のような実行で、確認できます。
% ./postal_xmlrpc_client 1008000 fault: 405: No address for postcode
XML-RPC はシンプルで軽量なプロトコルです。 郵便番号検索のようなデータ量の少ないアプリケーションであれば、 XML-RPC を使ったおもしろいサービスが提供できると思います。