基于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:接口定义
1
2
3
4
5
6
7
8
|
//gettime.h http://btdn.org 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:服务端代码
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
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
|
//gettime_Server.c http://btdn.org #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:客户端代码,这里就简单多了
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
33
|
//gettime_Client.c http://btdn.org #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:
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
|
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