nlib
exi/xml-rpc/xml-rpc.cpp

XML-RPCのリクエストを処理してレスポンスを生成するためのフロントエンドのサンプルです。

XML-RPCのリクエストの本体は例えば以下のようなXMLです。

<?xml version="1.0"?>
<methodCall>
<methodName>examples.getStateName</methodName>
<params>
<param>
<value><i4>41</i4></value>
</param>
</params>
</methodCall>

こう書くことで以下の手続きをどこかで実行しようとします。

examples.getStateName(41);

リクエストを送られた側は、リクエストを解釈して適切な関数を実行して返り値を返します。 これもXMLにして送り返す必要があって、それは例えば以下のようになります。

<?xml version="1.0"?>
<methodResponse>
<params>
<param>
<value><string>South Dakota</string></value>
</param>
</params>
</methodResponse>

こう書くことで、"South Dakota"という返り値を返しています。

このサンプルは、単一スレッドで動作するサンプルで、XML-RPCのリクエストとレスポンスをC++から簡単に生成したり取り扱うためのコードが実装されています。 バイナリXMLを用いることで、冗長なデータをやりとりするプロトコルのオーバーヘッドもいくらか削減することができます。

XML-RPCについては、XML-RPC仕様書の日本語訳が http://lowlife.jp/yasusii/stories/9.html にあります。

以下がサンプルのソースコードになります。

/*--------------------------------------------------------------------------------*
Project: CrossRoad
Copyright (C)Nintendo All rights reserved.
These coded instructions, statements, and computer programs contain proprietary
information of Nintendo and/or its licensed developers and are protected by
national and international copyright laws. They may not be disclosed to third
parties or copied or duplicated in any form, in whole or in part, without the
prior written consent of Nintendo.
The content herein is highly confidential and should be handled accordingly.
*--------------------------------------------------------------------------------*/
#include <vector>
#include "./xmlrpc_clientserver.h"
#include "./xmlrpc_value.h"
const int kBufSize = 1024 * 128;
unsigned char g_buf[kBufSize];
class Sum : public XmlRpcServerMethod {
public:
explicit Sum(XmlRpcServer* server) : XmlRpcServerMethod(N("MyFunc.Sum"), server) {}
virtual void Execute(const XmlRpcValue& params, XmlRpcValue* result);
};
void Sum::Execute(const XmlRpcValue& params, XmlRpcValue* result) {
// Sums the int/double values and returns the result as double.
double sum = 0;
const XmlRpcArray* ar = params.AsArray();
size_t n = ar->Size();
for (size_t i = 0; i < n; ++i) {
const XmlRpcValue* item = ar->Get(i);
if (!item) continue;
if (item->GetValueType() == XmlRpcValue::INT) {
sum += *item->AsInt();
} else if (item->GetValueType() == XmlRpcValue::DOUBLE) {
sum += *item->AsDouble();
}
}
result->SetDouble(sum);
}
class MyServer {
XmlRpcServer server_;
std::vector<XmlRpcServerMethod*> vec_;
public:
MyServer() {}
~MyServer() {
size_t n = vec_.size();
for (size_t i = 0; i < n; ++i) delete vec_[i];
}
void SetupMethods() {
Sum* sum = new (std::nothrow) Sum(&server_);
vec_.push_back(sum);
}
XmlRpcServer* GetServer() { return &server_; }
};
// Assumes the server in the distant place.....
MyServer g_server;
bool SampleMain(int, char**) {
g_server.SetupMethods();
XmlRpcClient client;
// This sample simplifies the server connections....
client.SetServer(g_server.GetServer());
// Calls the remotely defined function in the server:
// double MyFunc.Sum(list of int/double)
XmlRpcValue params;
params.SetArray();
params.AsArray()->Append()->SetDouble(1.0);
params.AsArray()->Append()->SetInt(2);
params.AsArray()->Append()->SetDouble(3.0);
nlib_printf("MyFunc.Sum([1.0, 2, 3.0])=");
// methodName = L"MyFunc.Sum"
// params = [1.0, 2, 3.0]
//
// Sends the XML below:
// <methodCall>
// <methodName>MyFunc.Sum</methodName>
// <params>
// <param><value><double>1.0</double></value></param>
// <param><value><i4>2</i4></value></param>
// <param><value><double>3.0</double></value></param>
// </params>
// </methodCall>
XmlRpcValue result;
XmlRpcCallResult future;
if (!client.Execute(N("MyFunc.Sum"), params, &future)) {
nlib_printf("client.Execute failed\n");
return false;
}
// Receives the response below:
// <methodResponse>
// <params>
// <param><value><double>6.0000</double></value></param>
// </params>
// </methodResponse>
if (!future.GetResult(&result)) {
nlib_printf("GetResult failed\n");
return false;
}
nlib_printf("%f\n", *result.AsArray()->Get(0)->AsDouble());
return true;
}
NLIB_MAINFUNC