基于C语言的soap网络接口远程方法调用

因为工作中需要用到远程方法调用(RPC?)。本人标准的懒人一枚。so向谷哥询问有没有现成的比较好用的东西可用。于是就发现了gsoap。目前最新版本是2.8.8,这个东西的神奇之处在于可以轻松的传送结构体,让你可以专心做数据处理,而且集成web service方便接口发布。而且再在前面放一web server做个反向代理,就算是分布式计算啦!

一看开发包我勒个去,toolkit 13.8M的大家伙啊,看了看发现其实有用的东西不多。因为里面有linux,mac,win三个平台的二进制工具,大片代码文件是编译其他平台gsoap工具用的,一般情况也用不到。做linux的C语言远程调用需要的只有这么几个:stdsoap2.c、stdsoap2.h、typemap.dat和bin/linux386下的soapcpp2、wsdl2h,取出这5个文件那个大压缩包就可以扔了。(-_-!这是为熟么捏?)

初期开发用c语言的.h头文件定义接口,后面生成的wsdl文件(Web Services Description Language)是基于xml格式的接口说明文件。可以挂在web上,供调用者做各种语言开发。

写个小demo,gettime.h:接口定义

//gettime.h http://blog.de3eb.cn
typedef struct _date
{
int timezone;
long dv;
}date;

int h__gettime(date* data);

然后执行命令

(调用者要先取得wsdl文件后执行./wsdl2h -c h.wsdl取得C的.h文件,-c生成纯c代码,下同)
./soapcpp2 -c gettime.h

gettime_Server.c:服务端代码

//gettime_Server.c http://blog.de3eb.cn
#include "soapH.h"
#include "h.nsmap"
#include <pthread.h>
#define TIME_ZONE 8
#define CST_TIME(a) (time(NULL)+a*60*60)
#define PORT 10001
#define BACKLOG 10

void* process_request(void* data);//处理线程
int http_post(struct soap *soap, const char *endpoint, const char *host, int port,const char *path, const char *action, size_t count);//post响应回调函数
int http_get(struct soap *soap);//get响应回调函数

int main()
{
 int m;
 pthread_t tid;
 struct soap soap,*tsoap;

 soap_init(&soap);
 //设定post、get回调函数,实现类似web service功能,展示wsdl发布调用接口
 soap.fget = http_get;
 soap.fpost = http_post;
 soap.accept_timeout = 5;//soap.里有各种timeout
 m = soap_bind(&soap, NULL, PORT, BACKLOG);//bind、accept同socket
 if (m < 0)
 {
 soap_print_fault(&soap, stderr);
 exit(-1);
 }
 while(1)
 {
 m = soap_accept(&soap);
 if (!soap_valid_socket(m))
 {
 if (soap.errnum)
 {
 soap_print_fault(&soap, stderr);
 continue;
 }
 fprintf(stderr, "server timed out\n");
 continue;
 }
 fprintf(stderr, "accepts socket %d connection from IP %ld.%ld.%ld.%ld\n", m, (soap.ip >> 24)&0xFF, (soap.ip >> 16)&0xFF, (soap.ip >> 8)&0xFF, soap.ip&0xFF);
 tsoap = soap_copy(&soap);
 if(!tsoap)
 {
 fprintf(stderr,"Error in soap_copy.\n");
 continue;
 }
 //创建线程处理可以使复杂方法有更好的响应能力
 if(0 != pthread_create(&tid,NULL,process_request,tsoap))
 {
 fprintf(stderr,"Error in pthread_create.\n");
 free(tsoap);
 continue;
 }
 }
 return 0;
}
//接口函数。这里准备返回的数据
int h__gettime(struct soap *soap, date* data)
{
 data->dv = CST_TIME(TIME_ZONE);
 data->timezone = TIME_ZONE;
 return SOAP_OK;
}
//request响应线程方法
void* process_request(void* data)
{
 soap_serve((struct soap*)data); // 会自动调用具体的接口函数
 soap_destroy((struct soap*)data); // dealloc C++ data
 soap_end((struct soap*)data); // dealloc data and clean up
 soap_done((struct soap*)data); // detach soap struct
 free(data);
 return NULL;
}
//这里简单把wsdl文件内容给浏览器,web service不是重点,意思一下。
int http_get(struct soap *soap)
{
 FILE*fd = NULL;
 fd = fopen("h.wsdl", "rb");//h.wsdl就是前面提到的发布用的接口说明文件
 if (!fd)
 return 404;//404你懂的
 soap->http_content = "text/xml";//xml文本类型
 soap_response(soap,SOAP_FILE);
 for(;;)
 {
 size_t r = fread(soap->tmpbuf,1, sizeof(soap->tmpbuf), fd);
 if (!r)
 break;
 if (soap_send_raw(soap, soap->tmpbuf, r))
 break;
 }
 fclose(fd);
 soap_end_send(soap);
 return SOAP_OK;
}
//同上
int http_post(struct soap *soap, const char *endpoint, const char *host, int port,const char *path, const char *action, size_t count)
{
 return http_get(soap);
}

gettime_Client.c:客户端代码,这里就简单多了

//gettime_Client.c http://blog.de3eb.cn
#include "soapH.h"
#include "h.nsmap"
#define SERVER_ADDR "http://localhost:10001"
#define ACTION ""
int main()
{
 date data;
 struct soap soap;
 struct tm *tm;
 char tm_buf[64];
 soap_init(&soap);
 //soap_set_namespaces(&soap, namespaces);
 memset(&data,0,sizeof(date));
 //调用远程方法
 if(SOAP_OK == soap_call_h__gettime(&soap, SERVER_ADDR, ACTION, &data))
 {
 tm = gmtime(&data.dv);
 strftime(tm_buf, sizeof(tm_buf), "%Y-%m-%d %H:%M:%S", tm);
 printf("timezone:%d\n"
 "time:%s\n"
 ,data.timezone
 ,tm_buf);
 }
 else
 {
 fprintf(stderr,"Error in soap_call_h__gettime.\n");
 }
 soap_destroy(&soap);
 soap_end(&soap);
 soap_done(&soap);
 return 0;
}

Makefile:

CC            = gcc
CFLAGS        = -Os -Wall
LDFLAGS       = -lpthread

COMMON_OBJS   = soapC.o stdsoap2.o
SERVER_OBJS   = gettime_Server.o soapServer.o
CLIENT_OBJS   = gettime_Client.o soapClient.o
OBJS          = $(COMMON_OBJS) $(SERVER_OBJS) $(CLIENT_OBJS)
TARGET_SERVER = gts
TARGET_CLIENT = gtc

all:$(TARGET_SERVER) $(TARGET_CLIENT)
	@echo "All Done"

$(TARGET_SERVER):$(SERVER_OBJS) $(COMMON_OBJS)
	$(CC) $(LDFLAGS) $(SERVER_OBJS) $(COMMON_OBJS) -o $(TARGET_SERVER)

$(TARGET_CLIENT):$(CLIENT_OBJS) $(COMMON_OBJS)
	$(CC) $(LDFLAGS) $(CLIENT_OBJS) $(COMMON_OBJS) -o $(TARGET_CLIENT)

$(OBJS):%.o:%.c
	$(CC) -c $(CFLAGS) $< -o $@

clean:
	rm -f $(TARGET_SERVER) $(TARGET_CLIENT) $(OBJS)

make一下就可以执行了,大功告成^-^!

[cz@myhost soap_demo]$ ./gts
accepts socket 4 connection from IP 10.0.2.2

[cz@myhost soap_demo]$ ./gtc
timezone:8
time:2012-03-05 16:25:59

发表评论?

3 条评论。

  1. 楼主,写的好详细,以后多来光顾一下,请多发表一些这样的文章。

  2. 是吗?这么明显,我是以一个过客的角度来写的评价,总之,写的很好,多多加油。

发表评论


请输入正确的验证码