以下為個人解讀,不保證完全正確。

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]

在我終於成功連線之後,藉此文章作個記錄。