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 にあります。

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

#include <vector>
#include "./xmlrpc_clientserver.h"
#include "./xmlrpc_value.h"
const int BUF_SIZE = 1024 * 128;
unsigned char g_Buf[BUF_SIZE];
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 m_Server;
std::vector<XmlRpcServerMethod*> m_Vec;
public:
MyServer() {}
~MyServer() {
size_t n = m_Vec.size();
for (size_t i = 0; i < n; ++i) delete m_Vec[i];
}
void SetupMethods() {
Sum* sum = new (std::nothrow) Sum(&m_Server);
m_Vec.push_back(sum);
}
XmlRpcServer* GetServer() { return &m_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