以下為個人解讀,不保證完全正確。
wsdl:定義web service的名稱、參數、型態
soap:傳輸資料的格式
兩個都是xml格式
wsdl放在server端
soap是server跟client規定傳輸資料的格式
gsoap toolkit:把wsdl轉換成 c / c++ 程式碼,免去軟體工程師需要撰寫處理soap封包。
簡單說明如何弄一組web service client & server
client在windows 7
server在vmware的ubuntu
vmware的網路設定成bridge(請自行孤狗)
第一步
首先弄個wsdl,如果不會寫,可以先把gsoap網站上的calc.wsdl抓下來,或是照他的指令弄出calc.h檔。
如果想跳過wsdl也是可以的,直接撰寫一個*.h檔,只是要知道撰寫的規則,這樣之後使用『soapcpp2』產生檔案才不會有問題。
因為我也不會寫wsdl,就直接照手冊,直接拿網站上的calc.wsdl來用。
> wsdl2h -vsgyc -o calc.c http://www.genivia.com/calc.wsdl
-o 是指定輸出檔名
-c 轉成純c的程式碼
其餘控制項請自行 wsdl2h -h
第二步
就是使用『soapcpp2』產生其他的程式碼。
> soapcpp2 -cTL calc.h
-c 也是純c的程式碼
-L 不要產生soapClientLib.c & soapServerLib.c。
目前用不到。
-T 會產生一個檔案『soapTester』。
這個檔案相當方便!因為他產生出來就是一個server的主程式!
只需要使用者自己寫函數實作內容。
爸特!!!!
如果server端是要弄multi-thread的話,還是要自己寫接收處理的部份。
其餘控制項也是自行參閱 soapcpp2 -h
經過處理,此時會有幾個檔案:
『calc.h』、『calc.nsmap』、『soapC.c』、『soapH.h』、『soapClient.c』、『soapServer.c』
『soapStub.h』、『soapTester.c』
『calc.add.req.xml』、『calc.add.res.xml』、『calc.div.req.xml』、『calc.div.res.xml』
『calc.sub.req.xml』、『calc.sub.res.xml』、『calc.mul.req.xml』、『calc.mul.res.xml』
『calc.pow.req.xml』、『calc.pow.res.xml』
如果照我的步驟跟指令,就會有上述的這些檔案。
*.xml是soap封包資料格式。之後再說明。
第三步
接下來先把server端完成。
先把gsoap toolkit裡面兩個檔案『stdsoap2.c』、『stdsoap2.h』拷貝到跟上述檔案相同的路徑下。
然後稍微修改 soapTester.c 。修改只是改函數實作內容。
/* soapTester.c Generated by gSOAP 2.8.3 from calc.h Copyright(C) 2000-2011, Robert van Engelen, Genivia Inc. All Rights Reserved. The generated code is released under one of the following licenses: 1) GPL or 2) Genivia's license for commercial use. This program is released under the GPL with the additional exemption that compiling, linking, and/or using OpenSSL is allowed. */ /* Stand-alone server auto-test code: Takes request from standard input or over TCP/IP socket and returns response to standard output or socket Compile: cc soapTester.c soapServer.c soapC.c stdsoap2.c Command line usage with redirect over stdin/out: > ./a.out < SomeTest.req.xml > ./a.out 12288 < SomeTest.req.xml Note: 12288 = SOAP_XML_INDENT | SOAP_XML_STRICT (see codes in stdsoap2.h) Command line usage to start server at port 8080: > a.out 12288 8080 */ #include <math.h> #include "calc.nsmap" #ifndef SOAP_DEFMAIN # define SOAP_DEFMAIN main /* redefine to use your own main() */ #endif int SOAP_DEFMAIN(int argc, char **argv) { struct soap *soap = soap_new1(argc > 1 ? atoi(argv[1]) : 0); if (argc <= 2) return soap_serve(soap); if (soap_valid_socket(soap_bind(soap, NULL, atoi(argv[2]), 100))) while (soap_valid_socket(soap_accept(soap))) { soap_serve(soap); soap_destroy(soap); soap_end(soap); } soap_free(soap); return 0; } /** Auto-test server operation ns2__add */ int ns2__add(struct soap *soap, double a, double b, double *result) { /* Echo request-response parameter */ *result = a + b; return SOAP_OK; } /** Auto-test server operation ns2__sub */ int ns2__sub(struct soap *soap, double a, double b, double *result) { /* Echo request-response parameter */ *result = a - b; return SOAP_OK; } /** Auto-test server operation ns2__mul */ int ns2__mul(struct soap *soap, double a, double b, double *result) { /* Echo request-response parameter */ *result = a * b; return SOAP_OK; } /** Auto-test server operation ns2__div */ int ns2__div(struct soap *soap, double a, double b, double *result) { /* Echo request-response parameter */ *result = a / b; return SOAP_OK; } /** Auto-test server operation ns2__pow */ int ns2__pow(struct soap *soap, double a, double b, double *result) { /* Echo request-response parameter */ *result = pow(a, b); return SOAP_OK; }
如果不改的話,原來程式碼會是『 *result = a; 』
還要記得include math.h
其實不改也可以,反正只要server跟client可以連線就好。
然後就編譯
> gcc -o calc.cgi soapC.c soapServer.c soapTester.c stdsoap2.c -lm
第四步
編譯client
先寫client主程式。
#include "soapH.h" #include "calc.nsmap" #include <stdio.h> #include <stdlib.h> int main(int argc, char** argv) { struct soap soap; double a = 2, b = 1, result; char* endpoint = NULL; if( argc == 4 ) { endpoint = argv[1]; a = atof( argv[2] ); b = atof( argv[3] ); } else if( argc == 3 ) { endpoint = "http://192.168.2.99/cgi-bin/calc.cgi"; a = atof( argv[1] ); b = atof( argv[2] ); } else return -1; soap_init(&soap); if( soap_call_ns2__add(&soap, endpoint, NULL, a, b, &result) == SOAP_OK ) { printf("%f + %f = %f\n", a, b, result); } else soap_print_fault(&soap, stderr); if( soap_call_ns2__sub(&soap, endpoint, NULL, a, b, &result) == SOAP_OK ) { printf("%f - %f = %f\n", a, b, result); } else soap_print_fault(&soap, stderr); if( soap_call_ns2__mul(&soap, endpoint, NULL, a, b, &result) == SOAP_OK ) { printf("%f * %f = %f\n", a, b, result); } else soap_print_fault(&soap, stderr); if( soap_call_ns2__div(&soap, endpoint, NULL, a, b, &result) == SOAP_OK ) { printf("%f / %f = %f\n", a, b, result); } else soap_print_fault(&soap, stderr); if( soap_call_ns2__pow(&soap, endpoint, NULL, a, b, &result) == SOAP_OK ) { printf("%f ^ %f = %f\n", a, b, result); } else soap_print_fault(&soap, stderr); soap_destroy(&soap); soap_end(&soap); soap_done(&soap); return 0; }
這裡有個地方要注意
原本在soapTester.c裡函數名稱是 ns2__add()
但是這邊卻是 soap_call_ns2__add()
這個函數的宣告可以在soapStub.h裡找到
ns2的2,以及_底線會因為wsdl腳本裡面某些定義不同而不同。
但是規則我不知道,還是自己看手冊。
然後code::block建立專案
把『calc.h』、『calc.nsmap』、『soapC.c』、『soapH.h』、『soapClient.c』、『soapStub.h』、『stdsoap2.c』、『stdsoap2.h』
還有上面的client 主程式加進去專案。記得要把libws2_32.a加到link。
第五步
使用web service
雖然gsoap的網站不推薦cgi方式連線,但是我還是講一下怎麼用。
- 用socket連線
在server端
> ./calc.cgi 1 888
第一個參數是soap mode,1只是我隨便填的,內容自己查手冊。
第二個參數是socket port number,這就不解釋了。
client端
> calcClient.exe http://192.168.2.99:888 3 2
第一個參數是server的IP以及對應的socket port number
第二個、第三個就沒啥好說的,只是丟兩個參數進去運算
- 用cgi方式連線
首先弄個apache server,然後把calc.cgi丟到 ~/cgi-bin 裡面。(這裡的路徑與apache安裝設定也自己孤狗)
server端不用執行,只要執行client端的程式!
>calcClient.exe 3 2
兩種連線方式的不同,我想大家應該可以從程式碼中看出不同的連線,gsoap處理方式的不同處。
我寫這篇的主要用意只是因為我cgi連線一直失敗,老是出現
Error 200 fault: SOAP-ENV:Client [no subcode] "Error 200" Detail: [no detail]
在我終於成功連線之後,藉此文章作個記錄。