xmlrpc远程函数式服务调用,C++作为服务端,python、C++客户端通过http进行调用
C++编写服务端
xmlrpc是一种通过网络进行过程调用的快速并且简单的方法。xmlrpc将过程调用需要的参数转换成xml文档,并以http协议发送给远端服务器,服务器将以xml协议将结果回复给客户端
服务端代码如下:
// xmlrcp.cpp : 此文件包含 "main" 函数。程序执行将在此处开始并结束。
//
#include <iostream>
#include <assert.h>
#include <xmlrpc-c/base.hpp>
#include <xmlrpc-c/registry.hpp>
#include <xmlrpc-c/server_abyss.hpp>
#include <codecvt>
#include <xmlrpc-c/base64.hpp>
#include <xmlrpc-c/base64.hpp>
class sampleAddMethod : public xmlrpc_c::method {
public:
sampleAddMethod() {}
void execute(xmlrpc_c::paramList const& paramList,
xmlrpc_c::value* const retvalP = NULL) {
int a = paramList.getInt(0);
int b = paramList.getInt(1);
*retvalP = xmlrpc_c::value_int(a + b);
/*std::cout << "struct success**" << std::endl;
assert(struct_param["first"].type() == xmlrpc_c::value::TYPE_INT);
assert(struct_param["first"].type() == xmlrpc_c::value::TYPE_INT);
xmlrpc_c::value_int value1 = struct_param["first"];
xmlrpc_c::value_int value2 = struct_param["second"];
int const addend = value1.cvalue();
int const adder = value2.cvalue();
paramList.verifyEnd(1);
*retvalP = xmlrpc_c::value_int(addend + adder);*/
}
};
class sampleAddMethod1 : public xmlrpc_c::method {
public:
sampleAddMethod1() {}
void
execute(xmlrpc_c::paramList const& paramList,
xmlrpc_c::value* const retvalP) {
// xmlrpc_c::cstruct tmp = paramList.getStruct(0);
// int const addend(paramList.getInt(0));
// int const adder(paramList.getInt(1));
// paramList.verifyEnd(2);
xmlrpc_c::cstruct struct_param = paramList.getStruct(0);
assert(struct_param["first"].type() == xmlrpc_c::value::TYPE_INT);
assert(struct_param["second"].type() == xmlrpc_c::value::TYPE_INT);
xmlrpc_c::value_int value1 = struct_param["first"];
xmlrpc_c::value_int value2 = struct_param["second"];
int const addend = value1.cvalue();
int const adder = value2.cvalue();
paramList.verifyEnd(1); //检测参数个数,如果位置1不是结尾标志(类似eof),那么抛出异常。
*retvalP = xmlrpc_c::value_int(addend + adder);
}
};
//测试乱码
class GetEncoding : public xmlrpc_c::method {
public:
GetEncoding() {}
void execute(xmlrpc_c::paramList const& paramList,
xmlrpc_c::value* const retvalP = NULL) {
std::vector<xmlrpc_c::value> result;
std::string inputstr = paramList.getString(0);
printf("输入字符串:%s \n", UTF8ToGBK(inputstr).c_str());
std::cout << "输入字符串" << UTF8ToGBK(inputstr) << std::endl;
std::wstring_convert<std::codecvt_utf8<wchar_t>> converter;
std::string utf8_str = converter.to_bytes(L"该符号在函数_wmain 中被引用");
//std::string c = base64Encode(utf8_str);
//std::string str = "该符号在函数_wmain 中被引用";
//std::vector<unsigned char> ss = xmlrpc_c::bytesFromBase64(str);
result.push_back(xmlrpc_c::value_string("hello"));
result.push_back(xmlrpc_c::value_string("world"));
//result.push_back(xmlrpc_c::value_bytestring(ss));
//std::vector<unsigned char> ss1 = xmlrpc_c::bytesFromBase64("English");
//result.push_back(xmlrpc_c::value_bytestring(ss1));
std::wstring_convert<std::codecvt_utf8<wchar_t>> converter1;
std::string utf8_str1 = converter1.to_bytes(L"中文汉字乱码");
result.push_back(xmlrpc_c::value_string(converter1.to_bytes(L"中文汉字乱码")));
//result.push_back(xmlrpc_c::value_string(converter.to_bytes(("English"))));
//result.push_back(xmlrpc_c::value_string(converter.from_bytes("中文汉字乱码")));
result.push_back(xmlrpc_c::value_string(StringCoding("中文汉字乱码 hahah")));
result.push_back(xmlrpc_c::value_string(StringCoding("中文汉字乱码")));
result.push_back(xmlrpc_c::value_string(StringCoding("中文汉字乱码 ")));
result.push_back(xmlrpc_c::value_string(StringCoding("Hello world 123")));
result.push_back(xmlrpc_c::value_string(StringCoding("123456789")));
result.push_back(xmlrpc_c::value_string(UnicodeToUTF8(StringToUnicode("中文汉字乱码"))));
result.push_back(xmlrpc_c::value_string(UnicodeToUTF8(StringToUnicode("Hello world"))));
//result.push_back(xmlrpc_c::value_string("中文汉字乱码"));
result.push_back(xmlrpc_c::value_string(UnicodeToUTF8((L"中文汉字乱码"))));
// byte[] byteData = Encoding.UTF8.GetBytes("你好中国");
//*retvalP = xmlrpc_c::value_string("http server connected");
*retvalP = xmlrpc_c::value_array(result);
}
std::string StringCoding(const std::string& str)
{
std::string result;
//获取缓冲区大小,并申请空间,缓冲区大小按字符计算
int len = MultiByteToWideChar(CP_ACP, 0, str.c_str(), str.size(), NULL, 0);
TCHAR* buffer = new TCHAR[len + 1];
//多字节编码转换成宽字节编码
MultiByteToWideChar(CP_ACP, 0, str.c_str(), str.size(), buffer, len);
buffer[len] = '\0'; //添加字符串结尾
try {
std::wstring_convert< std::codecvt_utf8<wchar_t> > wcv;
result = wcv.to_bytes(buffer);
}
catch (const std::exception& e) {
std::cerr << e.what() << std::endl;
}
//删除缓冲区并返回值
delete[] buffer;
return result;
}
std::string UTF8ToGBK(const std::string& str)
{
std::string result;
//获得临时变量的大小
int i = MultiByteToWideChar(CP_UTF8, 0, str.c_str(), -1, NULL, 0);
WCHAR* strSrc = new WCHAR[i + 1];
MultiByteToWideChar(CP_UTF8, 0, str.c_str(), -1, strSrc, i);
//获得临时变量的大小
i = WideCharToMultiByte(CP_ACP, 0, strSrc, -1, NULL, 0, NULL, NULL);
LPSTR szRes = new CHAR[i + 1];
WideCharToMultiByte(CP_ACP, 0, strSrc, -1, szRes, i, NULL, NULL);
result = szRes;
delete[]strSrc;
delete[]szRes;
return result;
}
std::wstring StringToUnicode(const std::string& str)
{
std::wstring result;
//获取缓冲区大小,并申请空间,缓冲区大小按字符计算
int len = MultiByteToWideChar(CP_ACP, 0, str.c_str(), str.size(), NULL, 0);
TCHAR* buffer = new TCHAR[len + 1];
//多字节编码转换成宽字节编码
MultiByteToWideChar(CP_ACP, 0, str.c_str(), str.size(), buffer, len);
buffer[len] = '\0'; //添加字符串结尾
//删除缓冲区并返回值
result.append(buffer);
delete[] buffer;
return result;
/*WCHAR sectionName[100];
memset(sectionName, 0, sizeof(sectionName));
MultiByteToWideChar(CP_ACP, 0, str.c_str(), str.length() + 1, sectionName, sizeof(sectionName) / sizeof(sectionName[0]));
return sectionName;*/
}
std::string UnicodeToUTF8(const std::wstring& wstr)
{
std::string ret;
try {
std::wstring_convert< std::codecvt_utf8<wchar_t> > wcv;
ret = wcv.to_bytes(wstr);
}
catch (const std::exception& e) {
std::cerr << e.what() << std::endl;
}
return ret;
}
std::wstring UTF8ToUnicode(const std::string& str)
{
std::wstring ret;
try {
std::wstring_convert< std::codecvt_utf8<wchar_t> > wcv;
ret = wcv.from_bytes(str);
}
catch (const std::exception& e) {
std::cerr << e.what() << std::endl;
}
return ret;
}
std::wstring ANSIToUnicode(const std::string& str)
{
std::wstring ret;
std::mbstate_t state = {};
const char* src = str.data();
size_t len = std::mbsrtowcs(nullptr, &src, 0, &state);
if (static_cast<size_t>(-1) != len) {
std::unique_ptr< wchar_t[] > buff(new wchar_t[len + 1]);
len = std::mbsrtowcs(buff.get(), &src, len, &state);
if (static_cast<size_t>(-1) != len) {
ret.assign(buff.get(), len);
}
}
return ret;
}
};
//测试连通性
class GetConnect : public xmlrpc_c::method {
public:
GetConnect() {}
void execute(xmlrpc_c::paramList const& paramList,
xmlrpc_c::value* const retvalP = NULL) {
printf("http服务连接成功\n");
std::wstring_convert<std::codecvt_utf8<wchar_t>> converter;
std::string utf8_str = converter.to_bytes(L"http 服务连接成功");
*retvalP = xmlrpc_c::value_string(utf8_str);
}
};
//发送数组
class SendArray : public xmlrpc_c::method {
public:
SendArray() {}
void execute(xmlrpc_c::paramList const& paramList,
xmlrpc_c::value* const retvalP = NULL) {
xmlrpc_c::carray array1 = paramList.getArray(0);
xmlrpc_c::carray array2 = paramList.getArray(1);
int length = array1.size();
std::vector<xmlrpc_c::value> result;
for (int i = 0; i < length; i++)
{
int no1 = xmlrpc_c::value_int(array1[i]);
int no2 = xmlrpc_c::value_int(array2[i]);
printf("数据1:%d,数据2:%d\n", no1, no2);
std::vector<xmlrpc_c::value> entrust_value;
entrust_value.push_back(array1[i]);
entrust_value.push_back(array2[i]);
xmlrpc_c::value arrayArray1 = xmlrpc_c::toValue(entrust_value);
result.push_back(arrayArray1);
}
*retvalP = xmlrpc_c::value_array(result);
}
};
int main()
{
//std::cout << "Hello World!\n";
xmlrpc_c::registry myRegistry;
xmlrpc_c::methodPtr const sampleAddMethodP = new sampleAddMethod();
myRegistry.addMethod("add", sampleAddMethodP);
xmlrpc_c::methodPtr const sampleAddMethodP1 = new sampleAddMethod1();
myRegistry.addMethod("add1", sampleAddMethodP1);
xmlrpc_c::methodPtr const send_array = new SendArray();
myRegistry.addMethod("send_array", send_array);
xmlrpc_c::methodPtr const coding = new GetEncoding();
myRegistry.addMethod("coding", coding);
//测试连通性
xmlrpc_c::methodPtr const get_connnect = new GetConnect();
myRegistry.addMethod("get_connnect", get_connnect);
// 端口号、日志文件、保持连接时间(应该是秒?)、最大连接数
xmlrpc_c::serverAbyss myAbyssServer(myRegistry, 8088,"",6000,100//,"xmlrpc_log" // TCP port on which to listen
//"/tmp/xmlrpc_log" // Log file
);
//开启服务
myAbyssServer.run(); // xmlrpc_c::serverAbyss.run() never returns
assert(false);
return 0;
}
继承xmlrpc_c::method类的基类实现了一个调用的 execute方法,代码中共有5个类继承了xmlrpc_c::method的基类,也就是有5个http的接口,分别是接收参数、返回数据、乱码处理、测试连通性、计算加法等的demo,main方法中
注册了这五个接口到http的服务中
xmlrpc_c::registry myRegistry;
xmlrpc_c::methodPtr const sampleAddMethodP = new sampleAddMethod();
myRegistry.addMethod("add", sampleAddMethodP);
xmlrpc_c::methodPtr const sampleAddMethodP1 = new sampleAddMethod1();
myRegistry.addMethod("add1", sampleAddMethodP1);
xmlrpc_c::methodPtr const send_array = new SendArray();
myRegistry.addMethod("send_array", send_array);
xmlrpc_c::methodPtr const coding = new GetEncoding();
myRegistry.addMethod("coding", coding);
//测试连通性
xmlrpc_c::methodPtr const get_connnect = new GetConnect();
myRegistry.addMethod("get_connnect", get_connnect);
然后设置端口号、日志文件、保持连接时间(应该是秒?)、最大连接数
xmlrpc_c::serverAbyss myAbyssServer(myRegistry, 8088,"",6000,100//,"xmlrpc_log" // TCP port on which to listen
//"/tmp/xmlrpc_log" // Log file
);
最后开启http服务:
//开启服务
myAbyssServer.run(); // xmlrpc_c::serverAbyss.run() never returns
C++编写客户端
C++客户端代码如下
#include <iostream>
#include <xmlrpc-c/base.hpp>
#include <xmlrpc-c/client_simple.hpp>
#include <codecvt>
#include <xmlrpc-c/server_abyss.hpp>
using namespace std;
using namespace xmlrpc_c;
string UTF8ToGBK(const string& str)
{
string result;
//获得临时变量的大小
int i = MultiByteToWideChar(CP_UTF8, 0, str.c_str(), -1, NULL, 0);
WCHAR* strSrc = new WCHAR[i + 1];
MultiByteToWideChar(CP_UTF8, 0, str.c_str(), -1, strSrc, i);
//获得临时变量的大小
i = WideCharToMultiByte(CP_ACP, 0, strSrc, -1, NULL, 0, NULL, NULL);
LPSTR szRes = new CHAR[i + 1];
WideCharToMultiByte(CP_ACP, 0, strSrc, -1, szRes, i, NULL, NULL);
result = szRes;
delete[]strSrc;
delete[]szRes;
return result;
}
wchar_t* StringToDword(const string& str)
{
wstring result;
//获取缓冲区大小,并申请空间,缓冲区大小按字符计算
int len = MultiByteToWideChar(CP_ACP, 0, str.c_str(), str.size(), NULL, 0);
TCHAR* buffer = new TCHAR[len + 1];
//多字节编码转换成宽字节编码
MultiByteToWideChar(CP_ACP, 0, str.c_str(), str.size(), buffer, len);
buffer[len] = '\0'; //添加字符串结尾
//删除缓冲区并返回值
result.append(buffer);
delete[] buffer;
wchar_t* inputStr = new wchar_t;
wcscpy(inputStr, result.c_str());
return inputStr;
}
int main()
{
string const serverUrl("http://localhost:8088/RPC2");
//string const methodName= "add";
//string const methodName("SuspectPersonManage");
xmlrpc_c::clientSimple myClient;
xmlrpc_c::value result;
myClient.call(serverUrl, "get_connnect", &result);
string ret_string = xmlrpc_c::value_string(result);
string ss = UTF8ToGBK(ret_string);
cout << ss << endl;
printf("返回信息:%s\n", UTF8ToGBK(ret_string).c_str());
xmlrpc_c::value coding_result;
//调用coding方法,w为宽字符串参数,
myClient.call(serverUrl, "coding","w", &coding_result, StringToDword("中文乱码"));
//返回值进行处理
std::vector<xmlrpc_c::value> vector_result;
xmlrpc_c::fromValue(vector_result, coding_result);
for (int i = 0; i < vector_result.size(); i++)
{
string out = xmlrpc_c::value_string(vector_result[i]);
cout <<"输出参数:"<< UTF8ToGBK(out) << endl;
}
xmlrpc_c::value add_result;
myClient.call(serverUrl, "add", "ii", &add_result, 5, 7);
int const sum((xmlrpc_c::value_int(add_result)));
// Assume the method returned an integer; throws error if not
cout << "Result of RPC (sum of 5 and 7): " << sum << endl;
xmlrpc_c::paramList paramLst;
xmlrpc_c::cstruct paramIn;
paramIn["first"] = xmlrpc_c::value_int(1);
paramIn["second"] = xmlrpc_c::value_int(2);
paramLst.addc(paramIn);
//myClient.call(serverUrl, methodName, paramLst, &result);
xmlrpc_c::value add1_result;
myClient.call(serverUrl, "add1", paramLst, &add1_result);
cout << "Result of RPC (sum of 1 and 2): " << xmlrpc_c::value_int(add1_result) << endl;
//发送集合参数
std::vector<int> input1;
std::vector<int> input2;
for (int i = 0; i < 10; i++)
{
input1.push_back(i);
input2.push_back(i+10);
}
xmlrpc_c::value arrayArray1 = xmlrpc_c::toValue(input1);
xmlrpc_c::value arrayArray2 = xmlrpc_c::toValue(input2);
xmlrpc_c::paramList paramLst1;
paramLst1.addc(arrayArray1);
paramLst1.addc(arrayArray2);
xmlrpc_c::value array_result;
myClient.call(serverUrl, "send_array", paramLst1, &array_result);
//返回值进行处理
std::vector<xmlrpc_c::value> send_result;
xmlrpc_c::fromValue(send_result, array_result);
for (int i = 0; i < send_result.size(); i++)
{
std::vector<xmlrpc_c::value> sub_send_result;
xmlrpc_c::fromValue(sub_send_result, send_result[i]);
for (int j = 0; j < sub_send_result.size(); j++)
{
cout << "输出参数:" << xmlrpc_c::value_int(sub_send_result[j]) << endl;
}
}
system("pause");
return 0;
}
客户端中演示了调用http服务的功能,使用http时代码中需要设置ip和端口号,然后直接通过调用函数的方式调用,演示了向服务端发送字符串、接收字符串数据、计算加法的示例。接收和发送中文字符串需要对字符编码进行处理,否则会出现乱码,UTF8ToGBK函数对接收到的字符串编码进行处理,StringToDword函数对发送的字符串编码进行处理,(服务端也会对接收到的字符串的编码进行处理,不处理的话服务端接收到的是乱码)
C++ 项目中包含xmlrpc的库,xmlrpc库可以参考 https://sourceforge.net/p/xmlrpc-c/code/HEAD/tree/
C++客户端和服务端输出结果
先运行服务端,在运行客户端,否则单独运行客户端会出错崩溃,代码中暂时没有对错误异常处理,输出的结果入截图,左边是服务端,右边是客户端,服务端中输出了客户端传入的数据,客户端中输出了服务端中返回的数据。
Python编写客户端
使用xmlrpc库进行编写,编写比较方便,代码如下,代码很简短,直接调用函数的方式调用http的服务
#!/usr/bin/python3
# -*- coding: utf-8 -*-
from xmlrpc.client import ServerProxy
server = ServerProxy("http://localhost:8088",encoding='utf-8',allow_none=True)
#调用coding方法,打印输出结果
print(server.coding('coding'))
#调用get_connnect方法
print(server.get_connnect())
#调用add方法
print('Result of RPC (sum of 5 and 7): {}'.format(server.add(5,7)))
print('Result of RPC (sum of 1 and 2): {}'.format(server.add1({'first':1,'second':3})))
#发送集合参数
input1 = []
input2 = []
for i in range(10):
input1.append(i)
input2.append(i+10)
ret = server.send_array(input1,input2)
print(ret)
运行结果如下
截图中包含客户端和服务端的输出结果
本文章主要参考以下博客,关于xmlrpc的介绍可以在下面的的博客中找到
https://blog.youkuaiyun.com/weixin_37348409/article/details/102327255
https://blog.youkuaiyun.com/qq_28453017/article/details/79931620
文章中C++工程代码和python代码已经上传到优快云中,具体可以查看下载链接https://download.youkuaiyun.com/download/zckui/37257772,(C++项目工程只能编译成x86、32位的,编译成64位的会出错,windows上暂不支持64位编译)