因为要访问一个JAVA做的一个服务,我这边的客户端可以用java做,但是后台程序都是用c写的,无法用c调用java方法,就是不能将返回结果写到内 存,我的后台程序也就不能直接从内存里读返回的结果,如果用jc调用java,只能将java作为一个整体(包含main函数),然后返回结果写到文件或 者数据库,后台程序再从文件或者数据库里读,这样程序执行速度会变慢,解决办法有两个:
1.用java调用c
就是将我的后台程序用java调用,然后再java调用服务,之后的程序肯定都是java写的了,总是整个程序都算是java程序了,这种方法不是很喜 欢,所以决定采用第二种方法。
2.c搭建一个service client
用c搭建一个service client的例子和方法都很少,有一种方法是gsoap方法,没用尝试,最后决定用axis2/c实现,axis2支持c语言,http: //axis.apache.org/,可以看到c版本,http://axis.apache.org/axis2/c/,阅读core版 块,http://axis.apache.org/axis2/c/core/,这里面介绍了安装方法,及用户手册。
(1)安装
http://axis.apache.org/axis2/c/core/docs/installationguide.html Linux Distribution
Binary Distribution axis2c-bin-1.6.0-linux.tar.gz
tar -zxvf axis2c-bin-1.6.0-linux.tar.gz
配置路径,根目录下,vim .bashrc添加如下内容:
export AXIS2C_HOME=/usr/axis2c-bin-1.6.0-linux
export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:${AXIS2C_HOME}/lib
保证每次开机都自动配置好路径。
上面路径是我本机上的路径,然后重启虚拟机。
测 试一下是否安装好了
启动一下服务
cd /usr/axis2c-bin-1.6.0-linux /bin
./axis2_http_server
这里包含几个服务的例子,接下来启动客户端。
cd /usr/axis2c-bin-1.6.0-linux /samples/bin
./echo
echo是一个服务,
./math
math是另一个服务。
测试应该没有问题。
(2)看手册
http://axis.apache.org/axis2/c/core/docs/axis2c_manual.html
了解基本概念,并自己根据手册里简单 的例子搭建一个服务端,客户端。
搭建服务端:
编译hello_svc.c,编译命令比较繁琐:gcc -shared -olibhello.so -I$AXIS2C_HOME/include/axis2-1.6.0/ -L$AXIS2C_HOME/lib -laxutil -laxis2_axiom -laxis2_parser -laxis2_engine -lpthread -laxis2_http_sender -laxis2_http_receiver hello_svc.c
在
/usr/axis2c-bin-1.6.0-linux /service 下创建一个hello文件夹,mkdir hello 如果名字错了,可以(mv 原名 新名),将编译生成的libhello.so拷贝到hello下,cp libhello.so /usr/axis2c-bin-1.6.0-linux /service/hello
将services.xml也拷贝到 hello下。
这样服务就搭好了,启动吧, cd /usr/axis2c-bin-1.6.0-linux /bin
./axis2_http_server
搭建客户端:
编译hello.c,编译命令:gcc -o hello -I$AXIS2C_HOME/include/axis2-1.6.0/ -L$AXIS2C_HOME/lib -laxutil -laxis2_axiom -laxis2_parser -laxis2_engine -lpthread -laxis2_http_sender -laxis2_http_receiver hello.c -ldl -Wl,--rpath -Wl,$AXIS2C_HOME/lib
./hello
会有返回结果的,这个例子比较简单。
3.自己的程序
他的请求只有一个节点,内容是“hello server”,但现在我的需求比较繁琐,是多个内容,那么应该如何使用里面的库函数实现呢,例子过于简单,其他的现有的实例又看不到源码,网上又找不到 分享的经验,没办法,一点点去看库函数,库函数都在include里面,但是看不到原函数内容,网上可以搜到一些函数代码。
遇到的主要问题:
(1) 问题a.
如何构建复杂的soap,soap包含三部分,一部分是必选的soap envelope,一部分是可选的 是soap header,一部分是必须的soap body,
构造的XML在soap body里,我的目标是构建这也一个body
<getOnlineList >
<authType>1</authType>
<authenticateStr>609AF3A36457C157549A3D6DB292FC50</authenticateStr>
<authenticateReserved></authenticateReserved>
<randomNum>12345678</randomNum>
<theTimestamp>1292336027000</theTimestamp>
</getOnlineList>
结构是这样的,但是最终传输的肯定会加上一些东西在里面。
nome element二者的关系?
请求或者应答都是一个OM,也就是说是一个node,这个node里可能又包含了很多node,有一个parent node,还有其它child node,每一个node有element,按照XML规范,一个node只能有一个element,但是手册里我看到可以有多个element,这个我 还没搞清楚是怎么回事,我是每个enode定义了一个element,这里面很多定义,我到现在还是有点混淆,这也导致了我费了很长时间去搞清楚这个问 题。
创建一个请求node
axiom_node_t* request_om_node = NULL;
axiom_element_t* request_om_ele = NULL;
赋值,加上命名空间(namespace)
axis2_char_t * om_str=NULL;
ns1=axiom_namespace_create( env,"http://soa.cernet.com/", "ns1");
request_om_ele = axiom_element_create(env, NULL,"getOnlineList",ns1, &request_om_node);
关于这个命名空间说两句,这个是必须设的,通过查看服务的wsdl可以看到命名空间,axiom_namespace_create函数的第三个参数一 定要赋值,什么值都行,但是必须要有值,最初我设置成了空,导致了我排查了一天的错误。
axiom_element_create的第二个参数指明parent node,但三个参数是
<getOnlineList >里的名字getOnlineList,第四个参数是命名空间,最后一个参数指明所属的node。
接下来是其他孩子node初始化和赋值,
axiom_node_t * type_om_node = NULL;
axiom_element_t * type_om_ele = NULL;
type_om_ele = axiom_element_create(env,request_om_node, "authType",NULL, &type_om_node);
axiom_element_set_text(type_om_ele, env,"1", type_om_node);
parent node为request_om_node, axiom_element_set_text函数用来赋值,其他孩子节点也都这么做了。
这样一个复杂的soap就构造完了,第一个问题解 决了,当然按照手册应该还有其他的构造方法,现在我还没弄清楚。
(2)问题b.
发送失败,返回http 415错误, Unsupported Media Type ,这是http头出现问题了
客户端在发送请求时,一般都需要在头部中添加一下 Content-Type 字段,用于指明所发送请求的内容的类型,例如使用 POST 方法发送表 单时 Content-Type 字段的值就为 application/x-www-form-urlencoded。因此,客户端如果收到 了 415 Unsupported Media Type 状态码,也就意味着它发送的请求的格式不被服务器接受,像在 XML-RPC 的调用过程 中,通信体格式应该为 application/xml 或 text/xml,服务端如果严格检查的话,应该在请求格式不为 application /xml 或 text/xml 时给出一个 415 Unsupported Media Type 的状态码,当然这个检查并不是必须的,XML 也 是文本内容,text/plain 的类型应该也是可接受的。
如何修改这个content-Type,费了好大劲找到库函数,
axis2_http_out_transport_info_set_content_type(
axis2_http_out_transport_info_t * info,
const axutil_env_t * env,
const axis2_char_t * content_type);
高兴之余,就是无奈,各种设置第一个参数都不对,我又搜到了这个函数的源码,想要初始化第一个参数,不太可能,里面很多东西不懂,从系统获取默认的第一 个参数值,我找到了这个函数,axis2_msg_ctx_get_out_transport_info ( msg_ctx , env),这个函数的一个参数也不懂,也不知道怎么赋值,最终放弃了,后来又发现一个库函数,就是初始化http头,但是里面还有的参数不知道该如何设 置,也放弃了。其实这些函数都是被系统其他的函数调用使用的,我的理解是他们不是面向用户的,就是我们根本无法改动,里面的参数都是系统里别的函数一级一 级传进来的,我们找到源头才能设置。一点启发就是通过看一些原函数代码知道了这个content-Type受soap版本的影响,
if(AXIS2_TRUE == is_soap11) { /* SOAP1.1 */ text/xml
AXIS2_ H T T P _ O U T _T R A N S P O R T _ I N F O _ S E T _ C O N T E N T _ T Y P E ( out_info, env, AXIS2_ H T T P _ HEADER_ACCEPT_TEXT_XML );
}
else { /* SOAP1.2 */ application/soap+xml
AXIS2_ H T T P _ O U T _T R A N S P O R T _ I N F O _ S E T _ C O N T E N T _ T Y P E ( out_info, env, AXIS2_ H T T P _ HEADER_ACCEPT_APPL_SOAP);
application/soap+xml,我们错误的原因是系统默认soap1.2,想要正确就改成soap1.1,soap有很多设置函数,最终找 到了axis2_options_set_soap_version(options, env, AXIOM_SOAP_11),这样就行了,Content-Type 变为text/xml。
(3)设置超时时间
发送请求之后,由于各种原因没有返回值,那么不希望一直在那等着,这样需要设置超时时间,这个函数也比较难找,我最初找到一个函数
axis2_http_client_set_timeout(
axis2_http_client_t * client,
const axutil_env_t * env,
int timeout_ms);
这个函数设置好之后做不了,原因不详。
后来又想起了soap设置是否有函数,结 果找到了
axis2_options_set_timeout_in_milli_seconds(options, env, 2000);
我设置的超时时间是2秒。
以后有问题就找soap设置函数吧,比较有效。
(4)解析返回结果
这部分还没有做,接下来会做,这部分的工作也不是很好懂。