探索ROS中的XML-RPC机制(二)客户端

HelloClient

客户端程序如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
#include "xmlrpcpp/XmlRpc.h"
#include <iostream>
using namespace XmlRpc;

int main(int argc, char* argv[])
{
if (argc != 3) {
std::cerr << "Usage: HelloClient serverHost serverPort\n";
return -1;
}
int port = atoi(argv[2]);
XmlRpc::setVerbosity(5);

// Use introspection API to look up the supported methods
XmlRpcClient c(argv[1], port);
XmlRpcValue noArg, result;
for (int i = 0; i < 2000; i++)
{
if (c.execute("system.listMethods", noArg, result))
std::cout << "\nMethods:\n " << result << "\n\n";
else
std::cout << "Error calling 'listMethods'\n\n";
}
// Call the HelloName method
oneArg[0] = "Chris";
if (c.execute("HelloName", oneArg, result))
std::cout << result << "\n\n";
else
std::cout << "Error calling 'HelloName'\n\n";

return 0;
}

看完服务端,客户端程序就更简单了,XmlRpcClient的构造函数是XmlRpcClient(const char* host, int port, const char* uri=0);,第一个参数是运行服务端程序的远方主机的IP(Server在本机时可用localhost),第二个是端口。

看类XmlRpcServer会发现自带了两个系统methods:system.listMethods和system.methodHelp,各对应一个类,我们可以用客户端直接调用这两个method。

在执行HelloName时需要使用另一个重要的类XmlRpcValue,对若干基本的C++的数据类型进行了封装,涉及的主要数据类型包括如下几个:

1
2
3
4
5
6
7
8
bool          asBool;
int asInt;
double asDouble;
struct tm* asTime;
std::string* asString;
BinaryData* asBinary; //typedef std::vector<char> BinaryData;
ValueArray* asArray; //typedef std::vector<XmlRpcValue> ValueArray;
ValueStruct* asStruct; //typedef std::map<std::string, XmlRpcValue> ValueStruct;

几个重要函数:

1
2
3
4
5
6
7
8
9
bool fromXml(std::string const& valueXml, int* offset);
std::string toXml() const;

bool valid() const
Type const &getType() const
// XML解码等一系列函数
bool boolFromXml(std::string const& valueXml, int* offset);
// XML编码等一系列函数
std::string boolToXml() const;


execute函数

执行远程服务端的方法,参数含义一目了然。如果请求发送成功且受到结果,函数返回true。
这是个同步(阻塞)的实现,直到客户端收到回复或错误才返回,否则一直阻塞。使用isFault()函数判断结果是否错误。

大致说一下函数的流程,先是执行setupConnection,与服务端建立连接.然后是生成要发送的XML文本在函数XmlRpcClient::generateRequest,处理了method和参数值.

客户端执行execute后,事件分发器执行XmlRpcClient::handleEvent,处理服务端的回复,主要调用下面三个函数:

  • 首先是XmlRpcClient::writeRequest(),日志从attempt 1 到 XmlRpcClient::writeRequest: wrote 258 of 258 bytes
  • 然后是XmlRpcClient::readHeader(),日志一直到 client read conten length: 122
  • 最后是XmlRpcClient::readResponse(),日志从XmlRpcClient::readResponse (read 122 bytes) 到 response的XML文本

现在回到execute函数,打出日志XmlRpcClient::execute: method HelloName completed

服务端回复的结果赋给result是在XmlRpcClient::parseResponse,这个函数里解析XML文本取出结果.

最后调用XmlRpcClient的析构函数,实际就是close()




整个通信过程中,客户端发的请求是:

1
2
3
4
5
6
7
8
9
POST /RPC2 HTTP/1.1                 # HTTP 1.1默认进行持久连接 Keep-Alive
User-Agent: XMLRPC++ 0.7 # 客户端浏览器类别
Host: 192.168.10.14:8888
Content-Type: text/xml # 这次类型是text/xml
Content-length: 138 # 发送请求的长度

<?xml version="1.0"?>
<methodCall><methodName>HelloName</methodName>
<params><param><value>Chris</value></param></params></methodCall>

服务端回复的是:

1
2
3
4
5
6
7
8
9
HTTP/1.1 200 OK
Server: XMLRPC++ 0.7 # web服务器软件名称
Content-Type: text/xml
Content-length: 122 # 响应体的长度

<?xml version="1.0"?>
<methodResponse><params><param>
<value>Hello, Chris</value>
</param></params></methodResponse>