1. 關於soap
在許多項目中團隊中,我們常常會聽到這樣的話:我們這里是用webservice交互的。而說話的場景往往就是交互對象雙方比較異構,所謂異構、即雙方是不同的開發語言、不同的運行環境等。比如常見的c/c++后台程序與java的web程序間的通信,當然這里的通信是網絡通信,如果是一體化單機系統內,可能第一反應是JNI方式了。
異構體系間的通信,就是webservice的基本應用場景。而soap(simple object access protocal)則是webservice在實際操作中需要遵守的協議,webservice的其它關鍵元素還有:WSDL, http, xml等。
2. 適用情況
如前所述、在一個項目中不同開發體系間希望交互信息,同時不願花大氣力自己做通信框架,產品需求定位為:快捷開發、穩定有效,沒有明顯大並發的需求,那么webservice是一個很好的選擇。
之所以排除大並發的應用場景,個人覺得大並發平台網絡環境復雜,在接口伸縮性和業務結合度方面,一般都是公司內部封裝通信框架更合適。另外webservice通信中攜帶的冗余信息太多也是一大詬病,所以大並發的場景,想想也算了。
3. C++ gsoap工具
Gsoap是c/c++在webservice開發中一個強大工具,c/c++er在做webservice開發一般都是用的這個利器。
從網上很容易找到gsoap的源碼,以我下載的gsoap-2.8為例。如果不用特別的研究編譯工具源碼,那么下載后需要用的東西是兩個地方:
1. 在\gsoap-2.8\gsoap\bin\win32目錄下有兩個編譯工具:wsdl2h.exe和soapcpp2.exe。
wsdl2h.exe:用於將wsdl文件轉換為c/c++使用的頭文件。
soapcpp2.exe:用於將上述頭文件轉換為c/c++項目使用的基礎代碼。包括客戶端代碼、服務端代碼、頭文件的wsdl描述。
2. 在\gsoap-2.8\gsoap目錄下有兩個文件:stdsoap2.cpp和stdsoap2.h。這兩個文件即為c++使用webservice通信的底層soap協議實現。
4. C++應用
對於C++來說,webservice就是一種RPC(Remote Procedure Call Protocol遠程過程調用協議)方式,既然是RPC,通俗的說、就是本地C++需要用到異地環境的方法,那么本地與異地雙方就有一個基本的方法列表,之后達到就像在調用本地方法一樣的調用異地方法。由此引出C++在webservice開發的基本過程:
C++服務端開發:
1)列出能夠提供的方法接口,寫入頭文件;
2) 用gsoap的soapcpp2.exe編譯工具將1)中頭文件編譯生成服務端代碼;
3) 將2)中生成的代碼引入到自己的服務端項目中,進行服務端業務開發,需要注意的是服務端必須實現1)中頭文件定義的方法。
c++客戶端開發:
1) 如果有服務方提供的wsdl文件,則用gsoap提供的wsdl2h.exe工具生成頭文件,然后同c++服務端開發的前兩步一樣,生成客戶端代碼;
如果C/S雙方都是C++開發,那么可以不需要wsdl的“介紹”,直接在上述c++服務端開發2)中,同時生成客戶端代碼,拿到這里用即可。
2) 將上述gsoap框架下的客戶端代碼引入到自己的客戶端項目中,就可以調用服務端方法了。
歸納:
C++的webservice開發,如果自己玩,可以不需要wsdl,但如果與其它體系一起協同通信,就需要wsdl(網絡服務描述語言)來描述頭文件的那些方法列表。Soap協議使得通信雙方不需要關心具體通信實現,雙發維護好提供業務的方法即可。
Gsoap是一個開源的soap封裝,從stdsoap2.cpp中可以看出其跨平台的實現,比如“#ifdef WIN32”這樣的痕跡。stdsoap2.h中代碼風格有值得學習的地方,比如條件預處理格式等,一個頭文件和一個cpp就實現了soap的協議封裝,短小精悍。\gsoap-2.8\gsoap\doc中文檔介紹朴素簡潔,建議以html方式查看,一目了然。
5. 簡單例子
本例子通過服務端提供一個字符串置反的方法,運行后在本機通過SoapUI測試客戶端調用。
C++服務端:
服務端工程,根據前面所述C++服務端開發步驟,首先給出頭文件reverse.h
int ns__reversestr(char *iStr, char **oStr); |
然后就只有一個main.cpp
#include "soapService.h" //服務方法的實現 int Service::reversestr(char *iStr, char **oStr) { if (NULL == iStr || NULL == oStr) { return this->error; } int strLen = strlen(iStr); *oStr = (char*)soap_malloc(this, strLen + 1); memset(*oStr, 0, strLen + 1); char *pOutBuf = *oStr; while (strLen-- > 0) { *(pOutBuf++) = *(iStr + strLen); } return this->error; } //服務入口,這里是最基本的服務形式 int main() { Service serv; serv.serve(); int port = 80; //服務端口,啟用前先用netstat查看下該端口是否被占用 if (serv.run(port)) { serv.soap_stream_fault(std::cerr); exit(-1); } return 0; } |
SoapUI測試: