前一阵,想在fedora下用C++服务程序,PHP前台程序,实现一个系统。选择封装完整HTTP协议通信,且实现多线程的程序库:ulxmlrpcpp。但是这个库只封装c++的客户端和服务端,所以决定在客户端使用PHP xml-rpc。然而两个库并不能完全通信,需要修改一些源码(只修改ulxmlrpcpp代码,所以需要重新编译ulxmlrpcpp,而phpxmlrpc不需要修改)。
1.程序下载:(linux)
ulxmlrpcpp-1.7.5:http://download.csdn.net/detail/liuzhengjian123/4497109
expat-2.1.0:http://download.csdn.net/detail/liuzhengjian123/4497115
phpxmlrpc-3.0.0:http://download.csdn.net/detail/liuzhengjian123/4497119
2.程序安装:(linux)
ulxmlrpcpp和expat的安装都是使用命令:
./confingure
make
make install
注意:安装ulxmlrpcpp前需要先安装expat,还要修改部分代码(详细参考后面)。
phpxmlrpc无需安装,只要将lib目录下的三个文件xmlrpc.inc、xmlrpcs.inc和xmlrpc_wrappers.inc放到php包含目录下即可。如果只编写客户端程序,那么只需要xmlrpc.inc就够了。
3.ulxmlrpcpp代码修改:
1)ulxr_config.h文件:添加如下宏定义,或注释部分宏
#define ULXR_VERSION "1.7.5" #define ULXR_DATADIR "./" //#define ULXR_UNICODE 12)ulxmlrpcpp.h文件:取消下面宏定义的注释
#define ULXR_ENFORCE_NON_PERSISTENT3)ulxr_http_protocol.cpp文件:注释掉573和574行
//else //setPersistent(true);4.修改原因:
1.修改之前可能会根本无法通信,首先要编写好ulxmlrpcpp后台程序,添加调用函数。编写PHP前台程序,注意调用相应的函数,主机名 和端口:ulxmlrpcpp配置文件如下:
# IP for xmlrpc server xmlrpc_ip = localhost # Port for xmlrpc server xmlrpc_port = 32005 wbxml = 0 ssl = 0 secure = 0 chunked = 0 shutdown = 0 persistent = 0代码调试成功后可能还是无法通信的原因可能是SELinux的问题,前台会提示错误信息:
(13)Permission denied这个是SELinux的访问控制权限的问题,执行下面命令就可以解决:
# setsebool -P httpd_can_network_connect 1
2.代码调试正确之后,可能会出现服务端可以收到调用请求,但是而且回答也正常发送,但是前台PHP无法解析回答的xml,而求PHP页面一直在等待,超时后才结束。错误代码:
Code: 2 Reason: 'Invalid return payload: enable debugging to examine incoming payload (XML error: Invalid document end at line 2, column 1)'而且打开PHP调试信息函数: $c->setDebug(1); 会发现PHP页面会显示收到两个http回复包:
---GOT---
HTTP/1.1 200 OK ------------------(第一个回复包,看上去正常)
Proxy-Connection: Keep-Alive
Content-Type: text/xml
Content-Length: 130
X-Powered-By: ulxmlrpcpp/1.7.5
Server: localhost
Date: Mon Aug 13 20:15:57 2012
<?xml version="1.0" encoding="utf-8"?><methodResponse><params><param><value><i4>23</i4></value></param></params></methodResponse>
HTTP/1.1 200 OK --------------------(第二个回复包,超时错误提示)
Connection: Keep-Alive
Content-Type: text/xml
Content-Length: 306
X-Powered-By: ulxmlrpcpp/1.7.5
Server: localhost
Date: Mon Aug 13 20:16:07 2012
<?xml version="1.0" encoding="utf-8"?><methodResponse><fault><value><struct><member><name>faultCode</name><value><i4>500</i4></value></member><member><name>faultString</name><value><string>Timeout while attempting to read (using select).</string></value></member></struct></value></fault></methodResponse>
---END---
为什么会返回两个包呢,经过分析,服务端采用了连续通信方式,发送回复包后,并不断开TCP连接,一直等待前台回复,而前台PHP端并没有采用这种策略,并不会再次向客户端请求断开。所以服务端一直等到超时才主动断开连接,断开连接还向前台发送了一个超时错误提示的包(就是第二个包)。所以我们要让后台ulxmlrpcpp发送回复后就断开连接,及采用不连续的方式,所以要修改上面标题3 中的 2)和3),使其强制断开。
5.代码:
1)前台php示例代码:client.php
<html> <head><title>xmlrpc</title></head> <body> <h1>Getstatename demo</h1> <h2>Send a U.S. state number to the server and get back the state name</h2> <h3>The code demonstrates usage of the php_xmlrpc_encode function</h3> <?php include("xmlrpc.inc"); // Play nice to PHP 5 installations with REGISTER_LONG_ARRAYS off if(!isset($HTTP_POST_VARS) && isset($_POST)) { $HTTP_POST_VARS = $_POST; } if(isset($HTTP_POST_VARS["stateno"]) && $HTTP_POST_VARS["stateno"]!="") { $stateno=(integer)$HTTP_POST_VARS["stateno"]; $f=new xmlrpcmsg('testcall_in_class_dynamic', array(php_xmlrpc_encode($stateno)) ); print "<pre>Sending the following request:\n\n" . htmlentities($f->serialize()) . "\n\nDebug info of server data follows...\n\n"; $c=new xmlrpc_client("./RPC2", "localhost", 32005); $c->setDebug(1); $r=&$c->send($f); echo htmlentities($r->serialize()); if(!$r->faultCode()) { $v=$r->value(); print "</pre><br/>State number " . $stateno . " is " . htmlspecialchars($v->scalarval()) . "<br/>"; // print "<HR>I got this value back<BR><PRE>" . // htmlentities($r->serialize()). "</PRE><HR>\n"; } else { print "An error occurred: "; print "Code: " . htmlspecialchars($r->faultCode()) . " Reason: '" . htmlspecialchars($r->faultString()) . "'</pre><br/>"; } } else { $stateno = ""; } print "<form action=\"test.php\" method=\"POST\"> <input name=\"stateno\" value=\"" . $stateno . "\"><input type=\"submit\" value=\"go\" name=\"submit\"></form> <p>Enter a state number to query its name</p>"; ?> <hr/> <em>$Id: client.php 2 2009-03-16 20:22:51Z ggiunta $</em> </body> </html>2)后台C++示例代码:server.cpp
#include <ulxmlrpcpp/ulxmlrpcpp.h> // always first header #include <cstring> #include <cstdlib> #include <ulxmlrpcpp/ulxr_tcpip_connection.h> // first, don't move: msvc #include bug #include <ulxmlrpcpp/ulxr_ssl_connection.h> #include <ulxmlrpcpp/ulxr_http_protocol.h> #ifdef __WIN32__ #include <process.h> #endif #include "util.c" #ifdef ULXR_MULTITHREADED #ifdef __unix__ #include <pthread.h> #endif #endif #include <iostream> #include <cstring> #include <memory> #include <ulxmlrpcpp/ulxr_except.h> #include <ulxmlrpcpp/ulxr_signature.h> #include <ulxmlrpcpp/ulxr_mtrpc_server.h> #ifdef __WIN32__ const unsigned num_threads = 10; #else const unsigned num_threads = 100; #endif //////////////////////////////////////////////////////////////////////// #ifdef ULXR_MULTITHREADED ulxr::MultiThreadRpcServer *mtServer = 0; class TestWorker { public: TestWorker () { } ulxr::MethodResponse numThreads (const ulxr::MethodCall &/*calldata*/) { ulxr::MethodResponse resp; resp.setResult(ulxr::Integer(mtServer->numThreads())); return resp; } ulxr::MethodResponse shutdown (const ulxr::MethodCall &/*calldata*/) { ULXR_COUT << ULXR_PCHAR("TestWorker got signal to shut down\n"); ulxr::MethodResponse resp; resp.setResult(ulxr::Boolean(true)); ULXR_COUT << ULXR_PCHAR(" Terminating..\n"); mtServer->terminateAllThreads(); ULXR_COUT << ULXR_PCHAR(" Returning..\n"); return resp; } ulxr::MethodResponse testcall (const ulxr::MethodCall &calldata) { ulxr::Integer I = calldata.getParam(0); int p1 = I.getInteger(); ULXR_COUT << ULXR_PCHAR("TestWorker got call(") << p1 << ULXR_PCHAR(") \n"); ulxr::MethodResponse resp; resp.setResult(I); return resp; } }; #endif // ULXR_MULTITHREADED //////////////////////////////////////////////////////////////////////// int main(int argc, char **argv) { #ifdef ULXR_MULTITHREADED try { ulxr::intializeLog4J(argv[0]); ulxr::getLogger4J()->send(ULXR_PCHAR("DEBUG"), ULXR_PCHAR("mt_server started"), ULXR_GET_STRING(__FILE__), __LINE__); ulxr::CppString host = ULXR_PCHAR("localhost"); if (argc > 1) host = ULXR_GET_STRING(argv[1]); unsigned port = 32005; if (argc > 2) port = ulxr_atoi(argv[2]); bool wbxml = haveOption(argc, argv, "wbxml"); bool secure = haveOption(argc, argv, "ssl"); bool chunked = haveOption(argc, argv, "chunked"); bool shutme = haveOption(argc, argv, "shutdown"); bool persistent = haveOption(argc, argv, "persistent"); ulxr::CppString sec = ULXR_PCHAR("unsecured"); if (secure) sec = ULXR_PCHAR("secured"); ULXR_COUT << ULXR_PCHAR("Serving ") << sec << ULXR_PCHAR(" rpc requests at ") << host << ULXR_PCHAR(":") << port << std::endl; ULXR_COUT << ULXR_PCHAR("WBXML: ") << wbxml << std::endl << ULXR_PCHAR("Chunked transfer: ") << chunked << std::endl; std::auto_ptr<ulxr::TcpIpConnection> conn; #ifdef ULXR_INCLUDE_SSL_STUFF if (secure) { ulxr::SSLConnection *ssl = new ulxr::SSLConnection (true, host, port); ssl->setCryptographyData("password", "foo-cert.pem", "foo-cert.pem"); conn.reset(ssl); } else #endif #ifdef _MSC_VER { std::auto_ptr<ulxr::TcpIpConnection> temp(new ulxr::TcpIpConnection (true, host, port)); conn = temp; } #else conn.reset(new ulxr::TcpIpConnection (true, host, port)); #endif std::auto_ptr<ulxr::HttpProtocol> prot(new ulxr::HttpProtocol(conn.get())); prot->setChunkedTransfer(chunked); prot->setPersistent(persistent); if (persistent) conn->setTcpNoDelay(true); if (prot->isPersistent()) ULXR_COUT << ULXR_PCHAR("Using persistent connections\n") ; else ULXR_COUT << ULXR_PCHAR("Using non-persistent connections\n") ; ulxr::MultiThreadRpcServer server(prot.get(), num_threads, wbxml); mtServer = &server; TestWorker worker; server.addMethod(ulxr::make_method(worker, &TestWorker::testcall), ulxr::Signature(ulxr::Struct()), ULXR_PCHAR("testcall_in_class_dynamic"), ulxr::Signature(ulxr::Integer()), ULXR_PCHAR("Testcase with a dynamic method in a class")); server.addMethod(ulxr::make_method(worker, &TestWorker::shutdown), ulxr::Signature(ulxr::Boolean()), ULXR_PCHAR("testcall_shutdown"), ulxr::Signature(), ULXR_PCHAR("Testcase with a dynamic method in a class, shut down server, return old state")); server.addMethod(ulxr::make_method(worker, &TestWorker::numThreads), ulxr::Signature(ulxr::Integer()), ULXR_PCHAR("testcall_numthreads"), ulxr::Signature(), ULXR_PCHAR("Returns number of installed threads at startup")); unsigned started = server.dispatchAsync(); ULXR_COUT << ULXR_PCHAR("Started ") << started << ULXR_PCHAR(" threads for dispatching rpc requests\n"); if (shutme) { ULXR_COUT << ULXR_CHAR("sleep before shutting down\n"); #ifdef __unix__ usleep(5 * 1000 * 1000); #elif defined(__WIN32__) Sleep(5 * 1000); #else #error platform not supported #endif ULXR_COUT << ULXR_CHAR("shutdownAllThreads\n"); server.shutdownAllThreads(); } server.waitAsync(false, true); ULXR_COUT << ULXR_PCHAR("Done.\n"); } catch(ulxr::Exception& ex) { ULXR_COUT << ULXR_PCHAR("Error occured: ") << ULXR_GET_STRING(ex.why()) << std::endl; return 1; } catch(...) { ULXR_COUT << ULXR_PCHAR("Unknown error occured\n"); return 1; } ULXR_COUT << ULXR_PCHAR("Ready.") << std::endl; #else // ULXR_MULTITHREADED ULXR_COUT << ULXR_PCHAR("Multithreaded support disabled."); #endif return 0; }6.总结
本文重点就是第3点的代码修改,虽然需要修改的地方并不是很多,但是也让我花了好长时间去分析ulxmlrpcpp源代码,才找出错误原因的,希望能给大家带来一些帮助。