gsoap使用心得
最近換了個工作環境,現在在大望路這邊上班,呵,剛上班接到的任務就是熟悉gsoap!廢話少說,現在開始gSoap學習!
gSOAP簡單多線程服務器程序? http://blog.chinaunix.net/u1/55091/showart_430965.html
純c gSoap實現WebService???????????? http://hi.baidu.com/2sky2sea/blog/item/40ec5555680279c1b745ae9b.html ?
接下來我結合自己的實踐與理解,講講VC用gsoap下編寫webService和客戶端程序,有不對的地方還請大家指正,謝謝。
我以網上出現的實現一個簡單的加法函數為例,講講我在操作過程中遇到的問題。
一 服務器端
1.首先編寫 add.h文件:
1//gsoap?ns?service?name:?add
2//gsoap?ns?service?namespace:?http://localhost/add.wsdl
3//gsoap?ns?service?location:?http://localhost
4//gsoap?ns?service?executable:?add.cgi
5//gsoap?ns?service?encoding:?encoded
6//gsoap?ns?schema?namespace:?urn:add
7
8int?ns__add(?int?num1,?int?num2,?int*?sum?);
9
2.用gsoap/bin目錄下的soapcpp2.exe程序,生成一些文件。可以把soapcpp2.exe拷貝到一add.h目錄下,用cmd執行soapcpp2.exe add.h就可以,在這個目錄下會自動生成許多將來有用的文件,如add.namap,soapH.h,soapC.cpp,soapClient.cpp,soapServer.cpp等文件。soapcpp2.exe可以帶參數執行,具體執行soapcpp2.exe -h查看。
3.新建一個win32控制臺工程,加入wsock32.lib庫,將剛才生成的那些文件添加到工程中。然后編寫webserver.cpp主程序:
#include?"add.h"
#include?"add.nsmap"
int?main(int?argc,?char*?argv[])
{
????
????int?m,?s;?/*?master?and?slave?sockets?*/
????struct?soap?add_soap;
????soap_init(&add_soap);
????//soap_set_namespaces(&add_soap,?add_namespaces);
????
????if?(argc?<?2)
????{
????????printf("usage:?%s?<server_port>?\n",?argv[0]);
????????exit(1);
????}
????else
????{?
????????m?=?soap_bind(&add_soap,?NULL,?atoi(argv[1]),?100);
????????if?(m?<?0)
????????{
????????????soap_print_fault(&add_soap,?stderr);
????????????exit(-1);
????????}
????????
????????fprintf(stderr,?"Socket?connection?successful:?master?socket?=?%d\n",?m);
????????for?(?;?;?)
????????{?
????????????s?=?soap_accept(&add_soap);?
????????????if?(s?<?0)
????????????{?
????????????????soap_print_fault(&add_soap,?stderr);
????????????????exit(-1);
????????????}
????????????fprintf(stderr,?"Socket?connection?successful:?slave?socket?=?%d\n",?s);
????????????
????????????soap_serve(&add_soap);//該句說明該server的服務
????????????soap_end(&add_soap);
????????}
????}
????return?0;
}
//server端的實現函數與add.h中聲明的函數相同,但是多了一個當前的soap連接的參數
int?ns__add(struct?soap?*add_soap,?int?num1,?int?num2,?int?*sum)
{
????*sum?=?num1?+?num2;
????return?0;
}
4. 編譯這個程序,會提示錯誤,將gsoap_win32目錄下stdsoap2.cpp,stdsoap2.h文件加入工程,重新編譯如果還有錯誤,可能是你將add.h生成的文件添加入工程出錯的原因。實際上在編寫server程序時,無須帶Client的那些文件,還有帶Lib的文件也無須添加到工程中。再重新編譯應該就沒有問題了,啟動4567端口,在ie中輸入localhost:4567,如果顯示xml頁面,說明程序已經啟動。
二 對應的客戶端
1。客戶端程序代碼如下:
#include?<stdio.h>
#include?<stdlib.h>
#include?"soapH.h"
#include?"add.nsmap"
int?add(const?char*?server,?int?num1,?int?num2,?int?*sum);
int?main(int?argc,?char?**argv)?
{
????int?result?=?-1;
????char*?server="http://localhost:4567";
????int?num1?=?0;
????int?num2?=?0;
????int?sum?=?0;
????if(?argc?<?3?)
????{
????????printf("usage:?%s?num1?num2?\n",?argv[0]);
????????exit(0);
????}
????
????num1?=?atoi(argv[1]);
????num2?=?atoi(argv[2]);
????
????result?=?add(server,?num1,?num2,?&sum);
????if?(result?!=?0)
????{
????????printf("soap?err,errcode?=?%d\n",?result);
????}
????else
????{
????????printf("%d+%d=%d\n",?num1,?num2,?sum?);
????}
????return?0;
}
int?add(?const?char*?server,?int?num1,?int?num2,?int?*sum?)
{
????struct?soap?add_soap;
????int?result?=?0;
????soap_init(&add_soap);
//????soap_set_namespaces(&add_soap,?add_namespaces);
????
????//該函數是客戶端調用的主要函數,后面幾個參數和add.h中聲明的一樣,前面多了3個參數,函數名是接口函數名ns__add前面加上soap_call_
????soap_call_ns__add(?&add_soap,?server,?"",?num1,?num2,?sum?);
????if(add_soap.error)
????{
????????printf("soap?error:%d,%s,%s\n",?add_soap.error,?*soap_faultcode(&add_soap),?*soap_faultstring(&add_soap)?);
????????result?=?add_soap.error;
????}?
????soap_end(&add_soap);
????soap_done(&add_soap);
????return?result;
}
2.客戶端程序既可以新建一個新的win32控制臺程序,將剛才生成的nsmap,soapH.h,soapClient.h等文件加入工程,編譯既可。我是直接在原先工程中加入一客戶端代碼,將webserver.cpp文件移除,并且將soapServer.cpp等server端需要的文件移除,將soapClient.cpp等client端需要的cpp添加到工程,編譯既可。
3.啟動server程序,F5客戶端程序,經測試正常。
三 遇到的問題
1.server端可以編譯成CGI方式執行,而并不是綁定到某個端口,這種方式我沒有實踐。
if?(argc?<?2)?//?no?args:?assume?this?is?a?CGI?application?
???{?
??????soap_serve(&soap);?//?serve?request,?one?thread,?CGI?style?
??????soap_destroy(&soap);?//?dealloc?C++?data?
??????soap_end(&soap);?//?dealloc?data?and?clean?up?
} 2.在編譯服務器及客戶端程序時一開始對add.h生成的文件添加到工程,經常出現問題,需要自己不調試。特別是鏈接時段,server/client要與其生成的文件相對應,server調用生成的soapserver.cpp,client調用生成的soapclient.cpp文件。
3.多線程方式,在windows下建議用pthread_win32庫,這里給出多線程下的例子。
一?gSOAP需要的頭文件:
//gsoap?ns?service?name:?calc
//gsoap?ns?service?style:?rpc
//gsoap?ns?service?encoding:?encoded
//gsoap?ns?service?namespace:?http://127.0.0.1:8089/calc.wsdl
//gsoap?ns?service?location:?http://127.0.0.1:8089/cal
//gsoap?ns?schema??namespace:????urn:calc
int?ns__add(double?a,?double?b,?double?*result);
int?ns__sub(double?a,?double?b,?double?*result);
int?ns__mul(double?a,?double?b,?double?*result);
int?ns__div(double?a,?double?b,?double?*result);
int?ns__pow(double?a,?double?b,?double?*result);
二?多線程服務器關鍵代碼
#include?
#include??"calc.nsmap"
#include??"soapH.h"
/
///宏與全局變量的定義
#define??BACKLOG?(100)??
#define??MAX_THR?(10)???
#define??MAX_QUEUE?(1000)
pthread_mutex_t?queue_cs;????????????????????????//隊列鎖
pthread_cond_t??queue_cv;??????????????????????????//條件變量
SOAP_SOCKET?????queue[MAX_QUEUE];???//數組隊列
int???????????????????????????head?=0,?tail?=0;??????????//隊列頭隊列尾初始化?????????
//
//
void?*??????process_queue(void?*);????????//線程入口函數
int?????????enqueue(SOAP_SOCKET);??//入隊列函數
SOAP_SOCKET?dequeue(void);?????????//出隊列函數
//
//線程入口函數
void?*?process_queue(void?*?soap)
{
??struct?soap?*?tsoap?=?(struct?soap?*)soap;
??for(;;)
??{
????????tsoap->socket?=?dequeue();
????????if?(!soap_valid_socket(tsoap->socket))
???????{
?????????break;
????????}
????????soap_serve(tsoap);
????????soap_destroy(tsoap);
????????soap_end(tsoap);
??}
??return?NULL;
}
//入隊列操作
int?enqueue(SOAP_SOCKET?sock)
{
??int?status?=?SOAP_OK;
??int?next;
??pthread_mutex_lock(&queue_cs);
??next?=?tail?+1;
??if?(next?>=?MAX_QUEUE)?
????next?=?0;
??if?(next?==?head)?
??????status?=?SOAP_EOM;
??else
??{
????queue[tail]?=sock;
????tail?=?next;
??}
??pthread_cond_signal(&queue_cv);
??pthread_mutex_unlock(&queue_cs);
??return?status;
}
//出隊列操作
SOAP_SOCKET?dequeue()
{
??SOAP_SOCKET?sock;
??pthread_mutex_lock(&queue_cs);
???while?(head?==?tail?)
???{
??????????pthread_cond_wait(&queue_cv,&queue_cs);
???}
??sock?=?queue[head++];
??if?(head?>=?MAX_QUEUE)
????????{
????head?=0;
??}
??pthread_mutex_unlock(&queue_cs);
??return?sock;
}
//具體服務方法////
//加法的實現
int?ns__add(struct?soap?*soap,?double?a,?double?b,?double?*result)
{
??????*result?=?a?+?b;
??????return?SOAP_OK;
}?
//減法的實現
int?ns__sub(struct?soap?*soap,?double?a,?double?b,?double?*result)
{?
?????*result?=?a?-?b;
?????return?SOAP_OK;
}?
//乘法的實現
int?ns__mul(struct?soap?*soap,?double?a,?double?b,?double?*result)
{?
?????*result?=?a?*?b;
?????return?SOAP_OK;
}?
//除法的實現
int?ns__div(struct?soap?*soap,?double?a,?double?b,?double?*result)
{?
???if?(b)
???????*result?=?a?/?b;
???else
??{
?????????char?*s?=?(char*)soap_malloc(soap,?1024);
?????????sprintf(s,?"Can't">http://tempuri.org/">Can't?divide?%f?by?%f",?a,?b);
?????????return?soap_sender_fault(soap,?"Division?by?zero",?s);
??}
??return?SOAP_OK;
}?
//乘方的實現
int?ns__pow(struct?soap?*soap,?double?a,?double?b,?double?*result)
{?
??*result?=?pow(a,?b);
??if?(soap_errno?==?EDOM)?/*?soap_errno?和errorno類似,?但是和widnows兼容?*/
??{?
????char?*s?=?(char*)soap_malloc(soap,?1024);
????sprintf(s,?"Can't?take?the?power?of?%f?to??%f",?a,?b);
????sprintf(s,?"Can't">http://tempuri.org/">Can't?take?power?of?%f?to?%f",?a,?b);
????return?soap_sender_fault(soap,?"Power?function?domain?error",?s);
??}
??return?SOAP_OK;
}?
//
//主函數
int?main(int?argc,char?**?argv)
{
??struct?soap?ServerSoap;
?????//初始話運行時環境
????soap_init(&ServerSoap);
????//如果沒有參數,當作CGI程序處理
????if?(argc?<2)?
????{???????
???????????//CGI?風格服務請求,單線程
??????????soap_serve(&ServerSoap);
??????????//清除序列化的類的實例
?????????soap_destroy(&ServerSoap);
?????????//清除序列化的數據
????????soap_end(&ServerSoap);?????
???}else
???{
?????struct?soap?*?soap_thr[MAX_THR];
?????pthread_t?tid[MAX_THR];
?????int?i,port?=?atoi(argv[1]);
?????SOAP_SOCKET?m,s;
??????//鎖和條件變量初始化
?????pthread_mutex_init(&queue_cs,NULL);
?????pthread_cond_init(&queue_cv,NULL);
?????//綁定服務端口
????m?=?soap_bind(&ServerSoap,NULL,port,BACKLOG);
????//循環直至服務套接字合法
????while?(!soap_valid_socket(m))
???{
????????????????fprintf(stderr,"Bind?port?error!?");
????????????????m?=?soap_bind(&ServerSoap,NULL,port,BACKLOG);
????}
????fprintf(stderr,"socket?connection?successful?%d?",m);
????????????????
?????//生成服務線程
????for(i?=?0;?i?<MAX_THR;?i++)
???{
??????soap_thr[i]?=?soap_copy(&ServerSoap);
??????fprintf(stderr,"Starting?thread?%d?",i);
??????pthread_create(&tid[i],NULL,(void*(*)(void*))process_queue,(void*)soap_thr[i]);
????}
????????????????
????for(;;)
????{
??????//接受客戶端的連接
??????s?=?soap_accept(&ServerSoap);
??????if?(!soap_valid_socket(s))?
??????{
????????if?(ServerSoap.errnum)?
????????????????????????????????{
??????????soap_print_fault(&ServerSoap,stderr);
??????????continue;
????????}else
????????{
??????????fprintf(stderr,"Server?timed?out?");
??????????break;
????????}
??????}
???????//客戶端的IP地址
??????fprintf(stderr,"Accepted?connection?from?IP=?%d.%d.%d.%d?socket?=?%d?",
???????????????????????????????((ServerSoap.ip)>>24)&&0xFF,((ServerSoap.ip)>>16)&0xFF,((ServerSoap.ip)>>8)&0xFF,(ServerSoap.ip)&0xFF,(ServerSoap.socket));
??????//請求的套接字進入隊列,如果隊列已滿則循環等待
???????while(enqueue(s)?==?SOAP_EOM)
????????????????Sleep(1000);
????}
????//服務結束后的清理工作
????for(i?=?0;?i?<?MAX_THR;?i++)
????{
??????while?(enqueue(SOAP_INVALID_SOCKET)?==?SOAP_EOM)?
???????{
???????????Sleep(1000);
??????}
????}
????for(i=0;?i<?MAX_THR;?i++)
????{
??????fprintf(stderr,"Waiting?for?thread?%d?to?terminate?..",i);
??????pthread_join(tid[i],NULL);
??????fprintf(stderr,"terminated?");
??????soap_done(soap_thr[i]);
??????free(soap_thr[i]);
????}
????pthread_mutex_destroy(&queue_cs);
????pthread_cond_destroy(&queue_cv);
??}
????//分離運行時的環境
??soap_done(&ServerSoap);
??return?0;
}
前一階段寫gSOAP 的文章沒保存好,后來想寫的,越學越沒有寫的勇氣了,感覺自己很菜,但是現在感覺還是寫點就算給入門者一點提示吧。另外雖說這篇文章是自己寫的,但是卻感覺是東拼西湊的,有很多別人的東西了。
? ?看了我轉載的關于soap 的文章,大家想必對soap有所了解了吧,那么gSOAP是什么那?
gSOAP 是一個開源的項目,用它可以方便的使用c/c++地進行SOAP客戶端和服務器端編程,而不必了解xml和SOAP協議的細節。這樣使用者就可以專注于自 己的web service 客戶端或服務器端的編寫,而不用糾纏與其它細節。我第一次接觸這些東西,我對SOAP的理解是這樣的:以http協議為基本的通信協議,以xml文件形式 請求遠程服務,再以xml文件的形式返回執行結果,我理解的就這么簡單了,有啥不妥處,還請指教阿。
實踐一下才有理性認識,下面是我自己在windows下,具體說來就是用vc 6.0下編寫的一個很簡單的客戶端程序調用遠程的服務,來發送電子郵件,感覺很爽吧。
首先我們到?http://sourceforge.net/project/showfiles.php?group_id=52781?下載gSOAP下載工具集吧,不同的系統下用的gSOAP是不一樣的,根據需要下載了windows下的和linux下的。
gSOAP工具集不需要安裝,直接解壓就可以了。在/bin目錄下我們可以看到兩個可執行文件:
soapcpp2.exe: gSOAP編譯器,編譯頭文件生成服務器和客戶端都需要的 c/c++文件。?
wsdl2h.exe: 編譯wsdl文件生成c/c++頭文件。
工具就算準備好了。
其次,我們到?http://www.abysal.com/soap/AbysalEmail.wsdl?下載
wsdl文件,假設保存文件名為:AbysalEmail.wsdl。所謂的wsdl文件翻譯成中
文就是網絡服務描述文件了。我們用wsdl2h.exe工具來根據wsdl文件生成
c/c++頭文件,可以用-c選項是生成純c的頭文件,另外用-s選項是說明我們在
程序中不使用stl,注意了默認我們是適用stl的。
用如下命令:
wsdl2h??-o AbysalEmail.h AbysalEmail.wsdl
既可以生成我們需要的AbysalEmail.h頭文件了。這里文件名可以隨便起了。
將下載的gsoap的import里的stlvector.h中文件拷貝到當前的文件夾下,因為默認是使用stl的,所以需要它。
然后執行soapcpp2 命令來生成存根程序,用如下命令:
soapcpp2??-C AbysalEmail.h
-C??選項是只生成客戶端的,默認是生成客戶端和服務器端的,如果你在程序中使用了vector還要加上 –limport選項。
即可以生存客戶端存根程序和框架了。
soapClient.cpp:編譯客戶端的需要的存根例程。
soapC.cpp,soapH.h:用來序列化和反序列化c/c++不同數據類型。
soapServer.cpp: 編譯服務器端的需要的存根例程。
soapXXXProxy.h: 生成的代理類的頭文件,使用代理類時需要此文件。
本程序為soapSendEmailBindingProxy.h。
第三步,就是在vc中建個工程,設置如下:
在vc6中建立工程,其源文件為:sendMailClient.cpp soapC.cpp?
? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?soapClient.cpp? ?stdsoap2.cpp
頭文件為:? ?? ?AbysalEmail.h soapH.h soapStub.h stdsoap2.h? ?? ?
其他依賴文件為:basetsd.h??sendemailbinding.nsmp
stdsoap2.cpp stdsoap2.h是下載的gSOAP中包含的。
另外在所需要的庫中把wsock32.lib加上,gSOAP也是采用socket方式連接的。(添加方法:項目->屬性->連接器->輸入->附加依賴項 添加wsock32.lib)
其中sendMailClient.cpp為我寫的客戶端程序,程序如下:
#include "soapH.h"? ?? ?? ?? ?? ?? ?? ?? ???// 得到存根程序
#include "SendEmailBinding.nsmap"? ?? ?? ?? ?//得到名稱空間映射表
#include <iostream>
#include??<string>
#include "soapSendEmailBindingProxy.h"
using namespace std;
int main(int argc, char **argv)?
{
? ? ? ? struct soap email_soap;
? ? ? ? int result = -1;
? ?SendEmailBinding??EmailBind;? ?? ?? ?? ???//生成代理類對象
? ? _ns1__SendEmail??sendEmail;? ?? ?? ?? ???//web服務發送電子郵件對象
? ?_ns1__SendEmailResponse??Email_Response;??//web 服務返回發送結果對象
? ?string from = "mseaspring";
? ?string to? ?= "David";
? ?string sub = "Hello test!";
? ?sendEmail.From = &from;
? ?sendEmail.FromAddress = "?mseaspring@hotmail.com?";
? ?sendEmail.MsgBody = "I want to test a web service!";
? ?sendEmail.To = &to;
? ?sendEmail.ToAddress = "?mseaspring@gmail.com?";
? ?sendEmail.Subject = ?
? ? ? ? result = EmailBind.__ns1__SendEmail(&sendEmail,??&Email_Response);
? ? ? ? if (result != 0)
? ? ? ? {
? ? ? ? ? ? ? ? printf("soap error ,errcode = %d\n", result);
? ? ? ? }
? ? ? ? else
? ? ? ? {
? ? ? ?? ???cout<<"The result is :"<<Email_Response.ReturnCode<<endl;
? ? ? ? ? ? ? ? cout<<"恭喜你,郵件發送成功!"<<endl;
? ? ? ? }
? ? ? ? return 0;
}我程序中是采用代理類的方式編寫的程序,不用代理類的代碼如下:
#include "soapH.h"? ?? ?? ?? ?? ?? ?? ?? ???//??得到存根程序
#include "SendEmailBinding.nsmap"? ?? ?? ?? ?// 得到名稱空間映射表
#include <iostream>
#include??<string>
using namespace std;
int main(int argc, char **argv)?
{
? ? ? ? struct soap email_soap;
? ? ? ? //初始化gSoap運行時環境變量,只需初始化一次
? ? ? ? soap_init(&email_soap);
? ? ? ? int result = -1;
? ? ? ? //遠程web服務的endpoint URL .不要帶WSDL
? ?const char* server="http://www.abysal.com/soap/soapmail.wdtp";
? ?string from = "mseaspring";
? ?string to? ?= "David";
? ?string sub = "Hello test!";
? ?sendEmail.From = &from;
? ?sendEmail.FromAddress = "?mseaspring@hotmail.com?";
? ?sendEmail.MsgBody = "I want to test a web service!";
? ?sendEmail.To = &to;
? ?sendEmail.ToAddress = "?mseaspring@gmail.com?";
? ?sendEmail.Subject = ?
? ? //調用根據遠程服務產生函數的接口
result = soap_call___ns1__SendEmail(&email_soap, server, "", &sendEmail,??&Email_Response);
? ? ? ? if(email_soap.error)
? ? ? ? {
? ? ? ? ? ? ? ? //在stderr流中打印soap的錯誤信息
? ? ? ? ? ? ? ? soap_print_fault(&email_soap,stderr);
? ? ? ? ? ? ? ? result = email_soap.error;
? ? ? ? }
? ? soap_destroy(&email_soap);// 刪除反序列化類的實例,僅用于c++
? ? ? ? soap_end(&email_soap);? ? // 清空已經并行化的數據
? ? ? ? soap_done(&email_soap);? ?// 與gSOAP 環境相分離,關閉連接
? ? ? ? if (result != 0)
? ? ? ? {
? ? ? ? ? ? ? ? printf("soap error ,errcode = %d\n", result);
? ? ? ? }
? ? ? ? else
? ? ? ? {
? ? ? ?? ???cout<<"The result is :"<<Email_Response.ReturnCode<<endl;
? ? ? ? ? ? ? ? cout<<"恭喜你,郵件發送成功!"<<endl;
? ? ? ? }
? ? ? ? return 0;
}
你可能會問我怎么知道遠程服務的接口阿? 到soapStub.h中去找就可以了,至于代理類的使用,到代理類頭文件中一看便知。
好了,終于要寫完了,當然我們不僅可以編寫客戶端也可以編寫服務器端程序,至于服務器端,有興趣的可以自己看看gSOAP里面的文檔,也很簡單的,不過也要花點時間學習的了,呵呵。
如果對于上面程序,有誰沒調試成功聯系我,郵箱都寫在程序里那。呵呵。
gSOAP是一個夸平臺的,用于開發Web Service服務端和客戶端的工具,在Windows、Linux、MAC OS和UNIX下使用C和C++語言編碼,集合了SSL功能。
下載地址:http://sourceforge.net/projects/gsoap2
官方網站:http://genivia.com/Products/gsoap/index.html
對于Windows平臺下開發客戶端,首先下載最新的gsoap_win32_2.7.6c.zip包,具體在以下地址:http://optusnet.dl.sourceforge.net/sourceforge/gsoap2/gsoap_win32_2.7.6c.zip
首先查看gsoap的User's Guide,基本就能對gsoap有個全面的了解,通過閱讀Sample里的例子程序深入。然后搜索網上其它一些文章,比如:gSOAP簡單多線程服務器程序? http://blog.chinaunix.net/u1/55091/showart_430965.html
純c gSoap實現WebService???????????? http://hi.baidu.com/2sky2sea/blog/item/40ec5555680279c1b745ae9b.html ?
接下來我結合自己的實踐與理解,講講VC用gsoap下編寫webService和客戶端程序,有不對的地方還請大家指正,謝謝。
我以網上出現的實現一個簡單的加法函數為例,講講我在操作過程中遇到的問題。
一 服務器端
1.首先編寫 add.h文件:
1//gsoap?ns?service?name:?add
2//gsoap?ns?service?namespace:?http://localhost/add.wsdl
3//gsoap?ns?service?location:?http://localhost
4//gsoap?ns?service?executable:?add.cgi
5//gsoap?ns?service?encoding:?encoded
6//gsoap?ns?schema?namespace:?urn:add
7
8int?ns__add(?int?num1,?int?num2,?int*?sum?);
9
2.用gsoap/bin目錄下的soapcpp2.exe程序,生成一些文件。可以把soapcpp2.exe拷貝到一add.h目錄下,用cmd執行soapcpp2.exe add.h就可以,在這個目錄下會自動生成許多將來有用的文件,如add.namap,soapH.h,soapC.cpp,soapClient.cpp,soapServer.cpp等文件。soapcpp2.exe可以帶參數執行,具體執行soapcpp2.exe -h查看。
3.新建一個win32控制臺工程,加入wsock32.lib庫,將剛才生成的那些文件添加到工程中。然后編寫webserver.cpp主程序:
#include?"add.h"
#include?"add.nsmap"
int?main(int?argc,?char*?argv[])
{
????
????int?m,?s;?/*?master?and?slave?sockets?*/
????struct?soap?add_soap;
????soap_init(&add_soap);
????//soap_set_namespaces(&add_soap,?add_namespaces);
????
????if?(argc?<?2)
????{
????????printf("usage:?%s?<server_port>?\n",?argv[0]);
????????exit(1);
????}
????else
????{?
????????m?=?soap_bind(&add_soap,?NULL,?atoi(argv[1]),?100);
????????if?(m?<?0)
????????{
????????????soap_print_fault(&add_soap,?stderr);
????????????exit(-1);
????????}
????????
????????fprintf(stderr,?"Socket?connection?successful:?master?socket?=?%d\n",?m);
????????for?(?;?;?)
????????{?
????????????s?=?soap_accept(&add_soap);?
????????????if?(s?<?0)
????????????{?
????????????????soap_print_fault(&add_soap,?stderr);
????????????????exit(-1);
????????????}
????????????fprintf(stderr,?"Socket?connection?successful:?slave?socket?=?%d\n",?s);
????????????
????????????soap_serve(&add_soap);//該句說明該server的服務
????????????soap_end(&add_soap);
????????}
????}
????return?0;
}
//server端的實現函數與add.h中聲明的函數相同,但是多了一個當前的soap連接的參數
int?ns__add(struct?soap?*add_soap,?int?num1,?int?num2,?int?*sum)
{
????*sum?=?num1?+?num2;
????return?0;
}
4. 編譯這個程序,會提示錯誤,將gsoap_win32目錄下stdsoap2.cpp,stdsoap2.h文件加入工程,重新編譯如果還有錯誤,可能是你將add.h生成的文件添加入工程出錯的原因。實際上在編寫server程序時,無須帶Client的那些文件,還有帶Lib的文件也無須添加到工程中。再重新編譯應該就沒有問題了,啟動4567端口,在ie中輸入localhost:4567,如果顯示xml頁面,說明程序已經啟動。
二 對應的客戶端
1。客戶端程序代碼如下:
#include?<stdio.h>
#include?<stdlib.h>
#include?"soapH.h"
#include?"add.nsmap"
int?add(const?char*?server,?int?num1,?int?num2,?int?*sum);
int?main(int?argc,?char?**argv)?
{
????int?result?=?-1;
????char*?server="http://localhost:4567";
????int?num1?=?0;
????int?num2?=?0;
????int?sum?=?0;
????if(?argc?<?3?)
????{
????????printf("usage:?%s?num1?num2?\n",?argv[0]);
????????exit(0);
????}
????
????num1?=?atoi(argv[1]);
????num2?=?atoi(argv[2]);
????
????result?=?add(server,?num1,?num2,?&sum);
????if?(result?!=?0)
????{
????????printf("soap?err,errcode?=?%d\n",?result);
????}
????else
????{
????????printf("%d+%d=%d\n",?num1,?num2,?sum?);
????}
????return?0;
}
int?add(?const?char*?server,?int?num1,?int?num2,?int?*sum?)
{
????struct?soap?add_soap;
????int?result?=?0;
????soap_init(&add_soap);
//????soap_set_namespaces(&add_soap,?add_namespaces);
????
????//該函數是客戶端調用的主要函數,后面幾個參數和add.h中聲明的一樣,前面多了3個參數,函數名是接口函數名ns__add前面加上soap_call_
????soap_call_ns__add(?&add_soap,?server,?"",?num1,?num2,?sum?);
????if(add_soap.error)
????{
????????printf("soap?error:%d,%s,%s\n",?add_soap.error,?*soap_faultcode(&add_soap),?*soap_faultstring(&add_soap)?);
????????result?=?add_soap.error;
????}?
????soap_end(&add_soap);
????soap_done(&add_soap);
????return?result;
}
2.客戶端程序既可以新建一個新的win32控制臺程序,將剛才生成的nsmap,soapH.h,soapClient.h等文件加入工程,編譯既可。我是直接在原先工程中加入一客戶端代碼,將webserver.cpp文件移除,并且將soapServer.cpp等server端需要的文件移除,將soapClient.cpp等client端需要的cpp添加到工程,編譯既可。
3.啟動server程序,F5客戶端程序,經測試正常。
三 遇到的問題
1.server端可以編譯成CGI方式執行,而并不是綁定到某個端口,這種方式我沒有實踐。
if?(argc?<?2)?//?no?args:?assume?this?is?a?CGI?application?
???{?
??????soap_serve(&soap);?//?serve?request,?one?thread,?CGI?style?
??????soap_destroy(&soap);?//?dealloc?C++?data?
??????soap_end(&soap);?//?dealloc?data?and?clean?up?
} 2.在編譯服務器及客戶端程序時一開始對add.h生成的文件添加到工程,經常出現問題,需要自己不調試。特別是鏈接時段,server/client要與其生成的文件相對應,server調用生成的soapserver.cpp,client調用生成的soapclient.cpp文件。
3.多線程方式,在windows下建議用pthread_win32庫,這里給出多線程下的例子。
一?gSOAP需要的頭文件:
//gsoap?ns?service?name:?calc
//gsoap?ns?service?style:?rpc
//gsoap?ns?service?encoding:?encoded
//gsoap?ns?service?namespace:?http://127.0.0.1:8089/calc.wsdl
//gsoap?ns?service?location:?http://127.0.0.1:8089/cal
//gsoap?ns?schema??namespace:????urn:calc
int?ns__add(double?a,?double?b,?double?*result);
int?ns__sub(double?a,?double?b,?double?*result);
int?ns__mul(double?a,?double?b,?double?*result);
int?ns__div(double?a,?double?b,?double?*result);
int?ns__pow(double?a,?double?b,?double?*result);
二?多線程服務器關鍵代碼
#include?
#include??"calc.nsmap"
#include??"soapH.h"
/
///宏與全局變量的定義
#define??BACKLOG?(100)??
#define??MAX_THR?(10)???
#define??MAX_QUEUE?(1000)
pthread_mutex_t?queue_cs;????????????????????????//隊列鎖
pthread_cond_t??queue_cv;??????????????????????????//條件變量
SOAP_SOCKET?????queue[MAX_QUEUE];???//數組隊列
int???????????????????????????head?=0,?tail?=0;??????????//隊列頭隊列尾初始化?????????
//
//
void?*??????process_queue(void?*);????????//線程入口函數
int?????????enqueue(SOAP_SOCKET);??//入隊列函數
SOAP_SOCKET?dequeue(void);?????????//出隊列函數
//
//線程入口函數
void?*?process_queue(void?*?soap)
{
??struct?soap?*?tsoap?=?(struct?soap?*)soap;
??for(;;)
??{
????????tsoap->socket?=?dequeue();
????????if?(!soap_valid_socket(tsoap->socket))
???????{
?????????break;
????????}
????????soap_serve(tsoap);
????????soap_destroy(tsoap);
????????soap_end(tsoap);
??}
??return?NULL;
}
//入隊列操作
int?enqueue(SOAP_SOCKET?sock)
{
??int?status?=?SOAP_OK;
??int?next;
??pthread_mutex_lock(&queue_cs);
??next?=?tail?+1;
??if?(next?>=?MAX_QUEUE)?
????next?=?0;
??if?(next?==?head)?
??????status?=?SOAP_EOM;
??else
??{
????queue[tail]?=sock;
????tail?=?next;
??}
??pthread_cond_signal(&queue_cv);
??pthread_mutex_unlock(&queue_cs);
??return?status;
}
//出隊列操作
SOAP_SOCKET?dequeue()
{
??SOAP_SOCKET?sock;
??pthread_mutex_lock(&queue_cs);
???while?(head?==?tail?)
???{
??????????pthread_cond_wait(&queue_cv,&queue_cs);
???}
??sock?=?queue[head++];
??if?(head?>=?MAX_QUEUE)
????????{
????head?=0;
??}
??pthread_mutex_unlock(&queue_cs);
??return?sock;
}
//具體服務方法////
//加法的實現
int?ns__add(struct?soap?*soap,?double?a,?double?b,?double?*result)
{
??????*result?=?a?+?b;
??????return?SOAP_OK;
}?
//減法的實現
int?ns__sub(struct?soap?*soap,?double?a,?double?b,?double?*result)
{?
?????*result?=?a?-?b;
?????return?SOAP_OK;
}?
//乘法的實現
int?ns__mul(struct?soap?*soap,?double?a,?double?b,?double?*result)
{?
?????*result?=?a?*?b;
?????return?SOAP_OK;
}?
//除法的實現
int?ns__div(struct?soap?*soap,?double?a,?double?b,?double?*result)
{?
???if?(b)
???????*result?=?a?/?b;
???else
??{
?????????char?*s?=?(char*)soap_malloc(soap,?1024);
?????????sprintf(s,?"Can't">http://tempuri.org/">Can't?divide?%f?by?%f",?a,?b);
?????????return?soap_sender_fault(soap,?"Division?by?zero",?s);
??}
??return?SOAP_OK;
}?
//乘方的實現
int?ns__pow(struct?soap?*soap,?double?a,?double?b,?double?*result)
{?
??*result?=?pow(a,?b);
??if?(soap_errno?==?EDOM)?/*?soap_errno?和errorno類似,?但是和widnows兼容?*/
??{?
????char?*s?=?(char*)soap_malloc(soap,?1024);
????sprintf(s,?"Can't?take?the?power?of?%f?to??%f",?a,?b);
????sprintf(s,?"Can't">http://tempuri.org/">Can't?take?power?of?%f?to?%f",?a,?b);
????return?soap_sender_fault(soap,?"Power?function?domain?error",?s);
??}
??return?SOAP_OK;
}?
//
//主函數
int?main(int?argc,char?**?argv)
{
??struct?soap?ServerSoap;
?????//初始話運行時環境
????soap_init(&ServerSoap);
????//如果沒有參數,當作CGI程序處理
????if?(argc?<2)?
????{???????
???????????//CGI?風格服務請求,單線程
??????????soap_serve(&ServerSoap);
??????????//清除序列化的類的實例
?????????soap_destroy(&ServerSoap);
?????????//清除序列化的數據
????????soap_end(&ServerSoap);?????
???}else
???{
?????struct?soap?*?soap_thr[MAX_THR];
?????pthread_t?tid[MAX_THR];
?????int?i,port?=?atoi(argv[1]);
?????SOAP_SOCKET?m,s;
??????//鎖和條件變量初始化
?????pthread_mutex_init(&queue_cs,NULL);
?????pthread_cond_init(&queue_cv,NULL);
?????//綁定服務端口
????m?=?soap_bind(&ServerSoap,NULL,port,BACKLOG);
????//循環直至服務套接字合法
????while?(!soap_valid_socket(m))
???{
????????????????fprintf(stderr,"Bind?port?error!?");
????????????????m?=?soap_bind(&ServerSoap,NULL,port,BACKLOG);
????}
????fprintf(stderr,"socket?connection?successful?%d?",m);
????????????????
?????//生成服務線程
????for(i?=?0;?i?<MAX_THR;?i++)
???{
??????soap_thr[i]?=?soap_copy(&ServerSoap);
??????fprintf(stderr,"Starting?thread?%d?",i);
??????pthread_create(&tid[i],NULL,(void*(*)(void*))process_queue,(void*)soap_thr[i]);
????}
????????????????
????for(;;)
????{
??????//接受客戶端的連接
??????s?=?soap_accept(&ServerSoap);
??????if?(!soap_valid_socket(s))?
??????{
????????if?(ServerSoap.errnum)?
????????????????????????????????{
??????????soap_print_fault(&ServerSoap,stderr);
??????????continue;
????????}else
????????{
??????????fprintf(stderr,"Server?timed?out?");
??????????break;
????????}
??????}
???????//客戶端的IP地址
??????fprintf(stderr,"Accepted?connection?from?IP=?%d.%d.%d.%d?socket?=?%d?",
???????????????????????????????((ServerSoap.ip)>>24)&&0xFF,((ServerSoap.ip)>>16)&0xFF,((ServerSoap.ip)>>8)&0xFF,(ServerSoap.ip)&0xFF,(ServerSoap.socket));
??????//請求的套接字進入隊列,如果隊列已滿則循環等待
???????while(enqueue(s)?==?SOAP_EOM)
????????????????Sleep(1000);
????}
????//服務結束后的清理工作
????for(i?=?0;?i?<?MAX_THR;?i++)
????{
??????while?(enqueue(SOAP_INVALID_SOCKET)?==?SOAP_EOM)?
???????{
???????????Sleep(1000);
??????}
????}
????for(i=0;?i<?MAX_THR;?i++)
????{
??????fprintf(stderr,"Waiting?for?thread?%d?to?terminate?..",i);
??????pthread_join(tid[i],NULL);
??????fprintf(stderr,"terminated?");
??????soap_done(soap_thr[i]);
??????free(soap_thr[i]);
????}
????pthread_mutex_destroy(&queue_cs);
????pthread_cond_destroy(&queue_cv);
??}
????//分離運行時的環境
??soap_done(&ServerSoap);
??return?0;
}
============================================================================
(轉http://www.cppblog.com/yeqing/articles/12762.html)前一階段寫gSOAP 的文章沒保存好,后來想寫的,越學越沒有寫的勇氣了,感覺自己很菜,但是現在感覺還是寫點就算給入門者一點提示吧。另外雖說這篇文章是自己寫的,但是卻感覺是東拼西湊的,有很多別人的東西了。
? ?看了我轉載的關于soap 的文章,大家想必對soap有所了解了吧,那么gSOAP是什么那?
gSOAP 是一個開源的項目,用它可以方便的使用c/c++地進行SOAP客戶端和服務器端編程,而不必了解xml和SOAP協議的細節。這樣使用者就可以專注于自 己的web service 客戶端或服務器端的編寫,而不用糾纏與其它細節。我第一次接觸這些東西,我對SOAP的理解是這樣的:以http協議為基本的通信協議,以xml文件形式 請求遠程服務,再以xml文件的形式返回執行結果,我理解的就這么簡單了,有啥不妥處,還請指教阿。
實踐一下才有理性認識,下面是我自己在windows下,具體說來就是用vc 6.0下編寫的一個很簡單的客戶端程序調用遠程的服務,來發送電子郵件,感覺很爽吧。
首先我們到?http://sourceforge.net/project/showfiles.php?group_id=52781?下載gSOAP下載工具集吧,不同的系統下用的gSOAP是不一樣的,根據需要下載了windows下的和linux下的。
gSOAP工具集不需要安裝,直接解壓就可以了。在/bin目錄下我們可以看到兩個可執行文件:
soapcpp2.exe: gSOAP編譯器,編譯頭文件生成服務器和客戶端都需要的 c/c++文件。?
wsdl2h.exe: 編譯wsdl文件生成c/c++頭文件。
工具就算準備好了。
其次,我們到?http://www.abysal.com/soap/AbysalEmail.wsdl?下載
wsdl文件,假設保存文件名為:AbysalEmail.wsdl。所謂的wsdl文件翻譯成中
文就是網絡服務描述文件了。我們用wsdl2h.exe工具來根據wsdl文件生成
c/c++頭文件,可以用-c選項是生成純c的頭文件,另外用-s選項是說明我們在
程序中不使用stl,注意了默認我們是適用stl的。
用如下命令:
wsdl2h??-o AbysalEmail.h AbysalEmail.wsdl
既可以生成我們需要的AbysalEmail.h頭文件了。這里文件名可以隨便起了。
將下載的gsoap的import里的stlvector.h中文件拷貝到當前的文件夾下,因為默認是使用stl的,所以需要它。
然后執行soapcpp2 命令來生成存根程序,用如下命令:
soapcpp2??-C AbysalEmail.h
-C??選項是只生成客戶端的,默認是生成客戶端和服務器端的,如果你在程序中使用了vector還要加上 –limport選項。
即可以生存客戶端存根程序和框架了。
soapClient.cpp:編譯客戶端的需要的存根例程。
soapC.cpp,soapH.h:用來序列化和反序列化c/c++不同數據類型。
soapServer.cpp: 編譯服務器端的需要的存根例程。
soapXXXProxy.h: 生成的代理類的頭文件,使用代理類時需要此文件。
本程序為soapSendEmailBindingProxy.h。
第三步,就是在vc中建個工程,設置如下:
在vc6中建立工程,其源文件為:sendMailClient.cpp soapC.cpp?
? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?soapClient.cpp? ?stdsoap2.cpp
頭文件為:? ?? ?AbysalEmail.h soapH.h soapStub.h stdsoap2.h? ?? ?
其他依賴文件為:basetsd.h??sendemailbinding.nsmp
stdsoap2.cpp stdsoap2.h是下載的gSOAP中包含的。
另外在所需要的庫中把wsock32.lib加上,gSOAP也是采用socket方式連接的。(添加方法:項目->屬性->連接器->輸入->附加依賴項 添加wsock32.lib)
其中sendMailClient.cpp為我寫的客戶端程序,程序如下:
#include "soapH.h"? ?? ?? ?? ?? ?? ?? ?? ???// 得到存根程序
#include "SendEmailBinding.nsmap"? ?? ?? ?? ?//得到名稱空間映射表
#include <iostream>
#include??<string>
#include "soapSendEmailBindingProxy.h"
using namespace std;
int main(int argc, char **argv)?
{
? ? ? ? struct soap email_soap;
? ? ? ? int result = -1;
? ?SendEmailBinding??EmailBind;? ?? ?? ?? ???//生成代理類對象
? ? _ns1__SendEmail??sendEmail;? ?? ?? ?? ???//web服務發送電子郵件對象
? ?_ns1__SendEmailResponse??Email_Response;??//web 服務返回發送結果對象
? ?string from = "mseaspring";
? ?string to? ?= "David";
? ?string sub = "Hello test!";
? ?sendEmail.From = &from;
? ?sendEmail.FromAddress = "?mseaspring@hotmail.com?";
? ?sendEmail.MsgBody = "I want to test a web service!";
? ?sendEmail.To = &to;
? ?sendEmail.ToAddress = "?mseaspring@gmail.com?";
? ?sendEmail.Subject = ?
? ? ? ? result = EmailBind.__ns1__SendEmail(&sendEmail,??&Email_Response);
? ? ? ? if (result != 0)
? ? ? ? {
? ? ? ? ? ? ? ? printf("soap error ,errcode = %d\n", result);
? ? ? ? }
? ? ? ? else
? ? ? ? {
? ? ? ?? ???cout<<"The result is :"<<Email_Response.ReturnCode<<endl;
? ? ? ? ? ? ? ? cout<<"恭喜你,郵件發送成功!"<<endl;
? ? ? ? }
? ? ? ? return 0;
}我程序中是采用代理類的方式編寫的程序,不用代理類的代碼如下:
#include "soapH.h"? ?? ?? ?? ?? ?? ?? ?? ???//??得到存根程序
#include "SendEmailBinding.nsmap"? ?? ?? ?? ?// 得到名稱空間映射表
#include <iostream>
#include??<string>
using namespace std;
int main(int argc, char **argv)?
{
? ? ? ? struct soap email_soap;
? ? ? ? //初始化gSoap運行時環境變量,只需初始化一次
? ? ? ? soap_init(&email_soap);
? ? ? ? int result = -1;
? ? ? ? //遠程web服務的endpoint URL .不要帶WSDL
? ?const char* server="http://www.abysal.com/soap/soapmail.wdtp";
? ?string from = "mseaspring";
? ?string to? ?= "David";
? ?string sub = "Hello test!";
? ?sendEmail.From = &from;
? ?sendEmail.FromAddress = "?mseaspring@hotmail.com?";
? ?sendEmail.MsgBody = "I want to test a web service!";
? ?sendEmail.To = &to;
? ?sendEmail.ToAddress = "?mseaspring@gmail.com?";
? ?sendEmail.Subject = ?
? ? //調用根據遠程服務產生函數的接口
result = soap_call___ns1__SendEmail(&email_soap, server, "", &sendEmail,??&Email_Response);
? ? ? ? if(email_soap.error)
? ? ? ? {
? ? ? ? ? ? ? ? //在stderr流中打印soap的錯誤信息
? ? ? ? ? ? ? ? soap_print_fault(&email_soap,stderr);
? ? ? ? ? ? ? ? result = email_soap.error;
? ? ? ? }
? ? soap_destroy(&email_soap);// 刪除反序列化類的實例,僅用于c++
? ? ? ? soap_end(&email_soap);? ? // 清空已經并行化的數據
? ? ? ? soap_done(&email_soap);? ?// 與gSOAP 環境相分離,關閉連接
? ? ? ? if (result != 0)
? ? ? ? {
? ? ? ? ? ? ? ? printf("soap error ,errcode = %d\n", result);
? ? ? ? }
? ? ? ? else
? ? ? ? {
? ? ? ?? ???cout<<"The result is :"<<Email_Response.ReturnCode<<endl;
? ? ? ? ? ? ? ? cout<<"恭喜你,郵件發送成功!"<<endl;
? ? ? ? }
? ? ? ? return 0;
}
你可能會問我怎么知道遠程服務的接口阿? 到soapStub.h中去找就可以了,至于代理類的使用,到代理類頭文件中一看便知。
好了,終于要寫完了,當然我們不僅可以編寫客戶端也可以編寫服務器端程序,至于服務器端,有興趣的可以自己看看gSOAP里面的文檔,也很簡單的,不過也要花點時間學習的了,呵呵。
如果對于上面程序,有誰沒調試成功聯系我,郵箱都寫在程序里那。呵呵。
?
soap_set_mode(&soap, SOAP_C_UTFSTRING);?
采用UTF-8的形式編碼發送和接收到的文字,可防止中文亂碼。?
總結
- 上一篇: Windows下编译和安装Boost库
- 下一篇: cmake Debug模式和Releas