用C++ ulxmlrpcpp 做服务端,PHP xmlrpc 做客户端,实现前后xml通信

来源:互联网 发布:html5程序员 表白网页 编辑:程序博客网 时间:2024/06/05 07:20

前一阵,想在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文件:添加如下宏定义,或注释部分宏

[cpp] view plaincopy
  1. #define ULXR_VERSION "1.7.5"  
  2. #define ULXR_DATADIR "./"  
  3. //#define ULXR_UNICODE 1  
2)ulxmlrpcpp.h文件:取消下面宏定义的注释
[cpp] view plaincopy
  1. #define ULXR_ENFORCE_NON_PERSISTENT  
3)ulxr_http_protocol.cpp文件:注释掉573和574行
[cpp] view plaincopy
  1. //else  
  2.     //setPersistent(true);  
4.修改原因

1.修改之前可能会根本无法通信,首先要编写好ulxmlrpcpp后台程序,添加调用函数。编写PHP前台程序,注意调用相应的函数,主机名 和端口:ulxmlrpcpp配置文件如下:

[cpp] view plaincopy
  1. # IP for xmlrpc server  
  2. xmlrpc_ip = localhost  
  3. # Port for xmlrpc server  
  4. xmlrpc_port = 32005  
  5. wbxml = 0  
  6. ssl = 0  
  7. secure = 0  
  8. chunked = 0  
  9. shutdown = 0  
  10. persistent = 0  
代码调试成功后可能还是无法通信的原因可能是SELinux的问题,前台会提示错误信息:
[html] view plaincopy
  1. (13)Permission denied  
这个是SELinux的访问控制权限的问题,执行下面命令就可以解决:
[html] view plaincopy
  1. # setsebool -P httpd_can_network_connect 1  

2.代码调试正确之后,可能会出现服务端可以收到调用请求,但是而且回答也正常发送,但是前台PHP无法解析回答的xml,而求PHP页面一直在等待,超时后才结束。错误代码:

[html] view plaincopy
  1. 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回复包:
[html] view plaincopy
  1. ---GOT---  
  2. HTTP/1.1 200 OK<span style="white-space:pre">   </span> ------------------(第一个回复包,看上去正常)  
  3. Proxy-Connection: Keep-Alive  
  4. Content-Type: text/xml  
  5. Content-Length: 130  
  6. X-Powered-By: ulxmlrpcpp/1.7.5  
  7. Server: localhost  
  8. Date: Mon Aug 13 20:15:57 2012  
  9.   
  10. <?xml version="1.0" encoding="utf-8"?><methodResponse><params><param><value><i4>23</i4></value></param></params></methodResponse>  
  11. HTTP/1.1 200 OK  --------------------(第二个回复包,超时错误提示)  
  12. Connection: Keep-Alive  
  13. Content-Type: text/xml  
  14. Content-Length: 306  
  15. X-Powered-By: ulxmlrpcpp/1.7.5  
  16. Server: localhost  
  17. Date: Mon Aug 13 20:16:07 2012  
  18.   
  19. <?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>  
  20. ---END---  
为什么会返回两个包呢,经过分析,服务端采用了连续通信方式,发送回复包后,并不断开TCP连接,一直等待前台回复,而前台PHP端并没有采用这种策略,并不会再次向客户端请求断开。所以服务端一直等到超时才主动断开连接,断开连接还向前台发送了一个超时错误提示的包(就是第二个包)。所以我们要让后台ulxmlrpcpp发送回复后就断开连接,及采用不连续的方式,所以要修改上面标题3 中的 2)和3),使其强制断开。

5.代码:

1)前台php示例代码:client.php

[php] view plaincopy
  1. <html>  
  2. <head><title>xmlrpc</title></head>  
  3. <body>  
  4. <h1>Getstatename demo</h1>  
  5. <h2>Send a U.S. state number to the server and get back the state name</h2>  
  6. <h3>The code demonstrates usage of the php_xmlrpc_encode function</h3>  
  7. <?php  
  8.     include("xmlrpc.inc");  
  9.   
  10.     // Play nice to PHP 5 installations with REGISTER_LONG_ARRAYS off  
  11.     if(!isset($HTTP_POST_VARS) && isset($_POST))  
  12.     {  
  13.         $HTTP_POST_VARS = $_POST;  
  14.     }  
  15.     if(isset($HTTP_POST_VARS["stateno"]) && $HTTP_POST_VARS["stateno"]!="")  
  16.     {  
  17.         $stateno=(integer)$HTTP_POST_VARS["stateno"];  
  18.         $f=new xmlrpcmsg('testcall_in_class_dynamic',  
  19.             array(php_xmlrpc_encode($stateno))  
  20.         );  
  21.         print "<pre>Sending the following request:\n\n" . htmlentities($f->serialize()) . "\n\nDebug info of server data follows...\n\n";  
  22.         $c=new xmlrpc_client("./RPC2""localhost", 32005);  
  23.         $c->setDebug(1);  
  24.         $r=&$c->send($f);  
  25.         echo htmlentities($r->serialize());  
  26.           
  27.         if(!$r->faultCode())  
  28.         {  
  29.             $v=$r->value();  
  30.             print "</pre><br/>State number " . $stateno . " is "  
  31.                 . htmlspecialchars($v->scalarval()) . "<br/>";  
  32.             // print "<HR>I got this value back<BR><PRE>" .  
  33.             //  htmlentities($r->serialize()). "</PRE><HR>\n";  
  34.         }  
  35.         else  
  36.         {  
  37.             print "An error occurred: ";  
  38.             print "Code: " . htmlspecialchars($r->faultCode())  
  39.                 . " Reason: '" . htmlspecialchars($r->faultString()) . "'</pre><br/>";  
  40.         }  
  41.     }  
  42.     else  
  43.     {  
  44.         $stateno = "";  
  45.     }  
  46.   
  47.     print "<form action=\"test.php\" method=\"POST\">  
  48. <input name=\"stateno\" value=\"" . $stateno . "\"><input type=\"submit\" value=\"go\" name=\"submit\"></form>  
  49. <p>Enter a state number to query its name</p>";  
  50.   
  51. ?>  
  52. <hr/>  
  53. <em>$Id: client.php 2 2009-03-16 20:22:51Z ggiunta $</em>  
  54. </body>  
  55. </html>  
2)后台C++示例代码:server.cpp
[cpp] view plaincopy
  1. #include <ulxmlrpcpp/ulxmlrpcpp.h>  // always first header  
  2.   
  3. #include <cstring>  
  4. #include <cstdlib>  
  5.   
  6. #include <ulxmlrpcpp/ulxr_tcpip_connection.h>  // first, don't move: msvc #include bug  
  7. #include <ulxmlrpcpp/ulxr_ssl_connection.h>  
  8. #include <ulxmlrpcpp/ulxr_http_protocol.h>  
  9.   
  10. #ifdef __WIN32__  
  11. #include <process.h>  
  12. #endif  
  13.   
  14. #include "util.c"  
  15.   
  16. #ifdef ULXR_MULTITHREADED  
  17.   
  18. #ifdef __unix__  
  19. #include <pthread.h>  
  20. #endif  
  21.   
  22. #endif  
  23.   
  24. #include <iostream>  
  25. #include <cstring>  
  26. #include <memory>  
  27.   
  28. #include <ulxmlrpcpp/ulxr_except.h>  
  29. #include <ulxmlrpcpp/ulxr_signature.h>  
  30. #include <ulxmlrpcpp/ulxr_mtrpc_server.h>  
  31.   
  32.   
  33. #ifdef __WIN32__  
  34. const unsigned num_threads = 10;  
  35. #else  
  36. const unsigned num_threads = 100;  
  37. #endif  
  38.   
  39. ////////////////////////////////////////////////////////////////////////  
  40.   
  41. #ifdef ULXR_MULTITHREADED  
  42.   
  43. ulxr::MultiThreadRpcServer *mtServer = 0;  
  44.   
  45. class TestWorker  
  46. {  
  47.  public:  
  48.   
  49.    TestWorker ()  
  50.    {  
  51.    }  
  52.   
  53.    ulxr::MethodResponse numThreads (const ulxr::MethodCall &/*calldata*/)  
  54.    {  
  55.      ulxr::MethodResponse resp;  
  56.      resp.setResult(ulxr::Integer(mtServer->numThreads()));  
  57.      return resp;  
  58.    }  
  59.   
  60.    ulxr::MethodResponse shutdown (const ulxr::MethodCall &/*calldata*/)  
  61.    {  
  62.      ULXR_COUT << ULXR_PCHAR("TestWorker got signal to shut down\n");  
  63.      ulxr::MethodResponse resp;  
  64.      resp.setResult(ulxr::Boolean(true));  
  65.      ULXR_COUT << ULXR_PCHAR(" Terminating..\n");  
  66.      mtServer->terminateAllThreads();  
  67.      ULXR_COUT << ULXR_PCHAR(" Returning..\n");  
  68.      return resp;  
  69.    }  
  70.   
  71.    ulxr::MethodResponse testcall (const ulxr::MethodCall &calldata)  
  72.    {  
  73.      ulxr::Integer I = calldata.getParam(0);  
  74.      int p1 = I.getInteger();  
  75.      ULXR_COUT << ULXR_PCHAR("TestWorker got call(")  << p1 << ULXR_PCHAR(") \n");  
  76.      ulxr::MethodResponse resp;  
  77.      resp.setResult(I);  
  78.      return resp;  
  79.    }  
  80. };  
  81.   
  82. #endif // ULXR_MULTITHREADED  
  83.   
  84. ////////////////////////////////////////////////////////////////////////  
  85.   
  86.   
  87. int main(int argc, char **argv)  
  88. {  
  89. #ifdef ULXR_MULTITHREADED  
  90.   
  91.   try  
  92.   {  
  93.     ulxr::intializeLog4J(argv[0]);  
  94.     ulxr::getLogger4J()->send(ULXR_PCHAR("DEBUG"),  
  95.                               ULXR_PCHAR("mt_server started"),  
  96.                               ULXR_GET_STRING(__FILE__),  
  97.                               __LINE__);  
  98.   
  99.     ulxr::CppString host = ULXR_PCHAR("localhost");  
  100.     if (argc > 1)  
  101.       host = ULXR_GET_STRING(argv[1]);  
  102.   
  103.     unsigned port = 32005;  
  104.     if (argc > 2)  
  105.       port = ulxr_atoi(argv[2]);  
  106.   
  107.     bool wbxml = haveOption(argc, argv, "wbxml");  
  108.     bool secure = haveOption(argc, argv, "ssl");  
  109.     bool chunked = haveOption(argc, argv, "chunked");  
  110.     bool shutme = haveOption(argc, argv, "shutdown");  
  111.     bool persistent = haveOption(argc, argv, "persistent");  
  112.   
  113.     ulxr::CppString sec = ULXR_PCHAR("unsecured");  
  114.     if (secure)  
  115.       sec = ULXR_PCHAR("secured");  
  116.   
  117.     ULXR_COUT << ULXR_PCHAR("Serving ") << sec << ULXR_PCHAR(" rpc requests at ") << host << ULXR_PCHAR(":") << port << std::endl;  
  118.     ULXR_COUT << ULXR_PCHAR("WBXML: ") << wbxml << std::endl  
  119.               << ULXR_PCHAR("Chunked transfer: ") << chunked << std::endl;  
  120.   
  121.     std::auto_ptr<ulxr::TcpIpConnection> conn;  
  122. #ifdef ULXR_INCLUDE_SSL_STUFF  
  123.     if (secure)  
  124.     {  
  125.       ulxr::SSLConnection *ssl = new ulxr::SSLConnection (true, host, port);  
  126.       ssl->setCryptographyData("password""foo-cert.pem""foo-cert.pem");  
  127.       conn.reset(ssl);  
  128.     }  
  129.     else  
  130. #endif  
  131. #ifdef _MSC_VER  
  132.   {  
  133.       std::auto_ptr<ulxr::TcpIpConnection> temp(new ulxr::TcpIpConnection (true, host, port));  
  134.     conn = temp;  
  135.     }  
  136. #else  
  137.       conn.reset(new ulxr::TcpIpConnection (true, host, port));  
  138. #endif  
  139.   
  140.     std::auto_ptr<ulxr::HttpProtocol> prot(new ulxr::HttpProtocol(conn.get()));  
  141.     prot->setChunkedTransfer(chunked);  
  142.     prot->setPersistent(persistent);  
  143.     if (persistent)  
  144.       conn->setTcpNoDelay(true);  
  145.   
  146.     if (prot->isPersistent())  
  147.       ULXR_COUT << ULXR_PCHAR("Using persistent connections\n") ;  
  148.     else  
  149.       ULXR_COUT << ULXR_PCHAR("Using non-persistent connections\n") ;  
  150.   
  151.     ulxr::MultiThreadRpcServer server(prot.get(), num_threads, wbxml);  
  152.     mtServer = &server;  
  153.   
  154.     TestWorker worker;  
  155.   
  156.     server.addMethod(ulxr::make_method(worker, &TestWorker::testcall),  
  157.                      ulxr::Signature(ulxr::Struct()),  
  158.                      ULXR_PCHAR("testcall_in_class_dynamic"),  
  159.                      ulxr::Signature(ulxr::Integer()),  
  160.                      ULXR_PCHAR("Testcase with a dynamic method in a class"));  
  161.   
  162.     server.addMethod(ulxr::make_method(worker, &TestWorker::shutdown),  
  163.                      ulxr::Signature(ulxr::Boolean()),  
  164.                      ULXR_PCHAR("testcall_shutdown"),  
  165.                      ulxr::Signature(),  
  166.                      ULXR_PCHAR("Testcase with a dynamic method in a class, shut down server, return old state"));  
  167.   
  168.     server.addMethod(ulxr::make_method(worker, &TestWorker::numThreads),  
  169.                      ulxr::Signature(ulxr::Integer()),  
  170.                      ULXR_PCHAR("testcall_numthreads"),  
  171.                      ulxr::Signature(),  
  172.                      ULXR_PCHAR("Returns number of installed threads at startup"));  
  173.   
  174.     unsigned started = server.dispatchAsync();  
  175.     ULXR_COUT << ULXR_PCHAR("Started ") << started << ULXR_PCHAR(" threads for dispatching rpc requests\n");  
  176.   
  177.     if (shutme)  
  178.     {  
  179.       ULXR_COUT << ULXR_CHAR("sleep before shutting down\n");  
  180. #ifdef __unix__  
  181.       usleep(5 * 1000 * 1000);  
  182. #elif defined(__WIN32__)  
  183.       Sleep(5 * 1000);  
  184. #else  
  185. #error platform not supported  
  186. #endif  
  187.       ULXR_COUT << ULXR_CHAR("shutdownAllThreads\n");  
  188.       server.shutdownAllThreads();  
  189.     }  
  190.   
  191.     server.waitAsync(falsetrue);  
  192.     ULXR_COUT << ULXR_PCHAR("Done.\n");  
  193.   }  
  194.   
  195.   catch(ulxr::Exception& ex)  
  196.   {  
  197.     ULXR_COUT << ULXR_PCHAR("Error occured: ") << ULXR_GET_STRING(ex.why()) << std::endl;  
  198.     return 1;  
  199.   }  
  200.   
  201.   catch(...)  
  202.   {  
  203.     ULXR_COUT << ULXR_PCHAR("Unknown error occured\n");  
  204.     return 1;  
  205.   }  
  206.   
  207.   ULXR_COUT << ULXR_PCHAR("Ready.") << std::endl;  
  208.   
  209. #else // ULXR_MULTITHREADED  
  210.   
  211.   ULXR_COUT << ULXR_PCHAR("Multithreaded support disabled.");  
  212.   
  213. #endif  
  214.   
  215.   return 0;  
  216. }  
6.总结

本文重点就是第3点的代码修改,虽然需要修改的地方并不是很多,但是也让我花了好长时间去分析ulxmlrpcpp源代码,才找出错误原因的,希望能给大家带来一些帮助。

原文地址:http://blog.csdn.net/liuzhengjian123/article/details/7861625


原创粉丝点击