ONVIF協議網路攝像機(IPC)客戶端程式開發(4):使用gSOAP生成Web Services框架程式碼
1. 專欄導讀
本專欄第一篇文章「專欄開篇」列出了專欄的完整目錄,按目錄順序閱讀,有助於你的理解,專欄前面文章講過的知識點(或程式碼段),後面文章不會贅述。為了節省篇幅,突出重點,在文章中展示的示例程式碼僅僅是關鍵程式碼,你可以在「專欄開篇」中獲取完整程式碼。
如有錯誤,歡迎你的留言糾正!讓我們共同成長!你的「點贊」或「打賞」是對我最大的支援和鼓勵!
2. 不要自己造輪子
ONVIF標準是使用SOAP方式實現的Web Services,本專欄上一篇文章已經介紹了什麼是Web Services,涉及很多概念,包括SOAP、HTTP、XML,RPC等等。辣麼多東東,全部要自己碼程式碼實現嗎?當然不用,我們不必自己造輪子,有現成的工具會幫我們自動生產大部分的程式碼框架。
這樣的工具有很多,比如:
- gSOAP工具,適用於C/C++語言開發。
- Apache CXF工具,適用於JAVA語言開發者。
我的專案採用C/C++語言,所以本文重點講解gSOAP。後面,網路攝像機(IPC)客戶端程式程式碼都是使用gSOAP工具自動生成的,所以必須對gSOAP工具必須有一個深入的理解,為此,我們先從簡單的例子開始理解。
3. gSOAP簡介
gSOAP有分商業版「commercial edition」和開源版「open source edition」,我撰寫本專欄用的gSOAP是開源版「gsoap_2.8.45」。
gSOAP 編譯工具提供了一個SOAP關於C/C++ 語言的實現,從而讓C/C++語言開發Web Services服務端或客戶端程式的工作變得輕鬆了很多。甚至,即使你對Web Services不甚瞭解都沒有關係,有了gSOAP這樣的工具,你也能開發基於SOAP方式實現的Web Services客戶端。
gSOAP到底會自動生成哪些框架程式碼,下圖中淺綠色框中的部分就是自動生成的程式碼。
4. gSOAP工具轉換原理
gSOAP工具根據WSDL文件,自動生成C/C++語言的客戶端/服務端框架程式碼。這其中有兩個工具很重要,wsdl2h和soapcpp2。wsdl2h工具根據WSDL文成C/C++標頭檔案,而soapcpp2工具則是根據該標頭檔案生成C/C++的框架原始碼。
gSOAP工具可以在Windows、Linux和Macosx作業系統下執行,gSOAP工具包中自帶有Windows和Macosx作業系統的wsdl2h和soapcpp2可執行檔案,而Linux作業系統的,得自己編譯。
通過實驗證實,用Windows和Linux工具生成的框架程式碼,是一樣樣的,沒有區別。
如何使用gSOAP,在gSOAP官網,或者在工具包gsoap\doc\soapdoc2.pdf文件中都有很詳細的說明,大家可以參考。下面我們通過「國內手機號碼歸屬地查詢」的例子,來演示如何使用gSOAP工具。
5. gSOAP演練例項:國內手機號碼歸屬地查詢
「國內手機號碼歸屬地查詢」免費WEB服務:
WEB服務地址: http://ws.webxml.com.cn/WebServices/MobileCodeWS.asmx
WSDL: http://ws.webxml.com.cn/WebServices/MobileCodeWS.asmx?wsdl
- 下載gSOAP工具,我的版本是「gsoap_2.8.45」。
建立一個資料夾MobileCode,從gSOAP工具中拷貝如下檔案和資料夾到MobileCode資料夾中,
gsoap_2.8.45\gsoap-2.8\gsoap\bin\win32\soapcpp2.exe gsoap_2.8.45\gsoap-2.8\gsoap\bin\win32\wsdl2h.exe gsoap_2.8.45\gsoap-2.8\gsoap\stdsoap2.c gsoap_2.8.45\gsoap-2.8\gsoap\stdsoap2.h gsoap_2.8.45\gsoap-2.8\gsoap\typemap.dat gsoap_2.8.45\gsoap-2.8\gsoap\import\ gsoap_2.8.45\gsoap-2.8\gsoap\custom\
最終效果如下:
啟動cmd.exe,確保當前路徑在剛才建立的MobileCode目錄下:
使用wsdl2h工具,根據WSDL產生標頭檔案,在cmd中執行以下命令:
wsdl2h.exe -o mobilecode.h -c -s -t typemap.dat http://ws.webxml.com.cn/WebServices/MobileCodeWS.asmx?wsdl
其中-c為產生純c程式碼,預設生成 c++程式碼;-s為不使用STL庫,-t為typemap.dat的標識。詳情可通過wsdl2h.exe -help檢視幫助。
這裡的WSDL檔案,可以在wsdl2h命令中線上下載,也可以先下載到本地,然後引用本地WSDL檔案,我這裡是採用線上下載方式。
使用soapcpp2工具,根據標頭檔案產生框架程式碼,在cmd中執行以下命令:
soapcpp2.exe -2 -C -c -x -Iimport -Icustom mobilecode.h
-2為生成SOAP 1.2版本的程式碼,-C為僅生成客戶端的程式碼(服務端的不要),-c生成C語言程式碼,詳情可使用soapcpp2.exe -help檢視幫助。
自動生成的原始碼檔案如下圖所示,
其中custom、import、wsdl2h.exe、soapcpp2.exe、typemap.dat、mobilecode.h、soapClientLib.c這些檔案已經沒用了,可以刪掉,最終剩下的檔案只有:
在soapStub.h檔案中,列出了「國內手機號碼歸屬地查詢」WEB服務的所有介面(Client-Side Call Stub Functions),我們的應用程式通過呼叫這些介面就成了,至於SOAP協議整個過程怎麼實現的,都在soapC.c和soapClient.c中,有興趣的可以去研究,沒興趣的就不管它了,懂得呼叫以下這幾個介面就可以了。
接下來,寫個main.c,通過soap_call___ns1__getMobileCodeInfo介面來查詢國內手機號碼歸屬地資訊,並將其打印出來,原始碼如下所示(例項程式碼已上傳網路:點選下載)。
#include <stdio.h> #include <stdlib.h> #include <string.h> #include "soapStub.h" #include "MobileCodeWSSoap.nsmap" void getMobileCodeInfo(char *mobileCode) { struct soap *soap = NULL; const char *endpoint = "http://ws.webxml.com.cn/WebServices/MobileCodeWS.asmx"; struct _ns1__getMobileCodeInfo req; struct _ns1__getMobileCodeInfoResponse resp; soap = soap_new(); // allocate and initalize a context soap_set_mode(soap, SOAP_C_UTFSTRING); // support multibyte string(for Chinese) memset(&req, 0x00, sizeof(req)); req.mobileCode = mobileCode; req.userID = NULL; if(SOAP_OK == soap_call___ns1__getMobileCodeInfo(soap, endpoint, NULL, &req, &resp)) { if (NULL != resp.getMobileCodeInfoResult) { printf("%s\n", resp.getMobileCodeInfoResult); } } soap_destroy(soap); // delete deserialized objects soap_end(soap); // delete allocated data soap_free(soap); // free the soap struct context data } int main(int argc, char **argv) { if (argc < 2) { return 0; } getMobileCodeInfo(argv[1]); return 0; }
第一次執行,如下圖所示,會出現亂碼:
這是由於WEB服務應答的歸屬地資訊中包含有UTF-8格式的中文導致的。SOAP協議採用HTTP傳輸協議+XML資料格式,規定XML字元編碼格式必須是UTF-8。為了解決這個問題:
一、在原始碼中加入soap_set_mode(soap, SOAP_C_UTFSTRING)語句,告知gSOAP底層程式碼,我們上層傳入的字元編碼格式已經是UTF-8,,內部就不參與轉碼的過程,WEB伺服器應答的UTF-8字元也都直接傳給上層,此時我們的main.c程式碼收到的應答也是UTF-8格式的資料。
二、cmd.exe環境預設的環境是「簡體中文GBK」,通過chcp命令就能查到,「活動內碼表936」代表的就是「簡體中文GBK」,在這種環境下列印UTF-8中文字元當然會亂碼,使用命令chcp 65001將控制檯的字符集改為UTF-8,「活動內碼表65001」代表的就是UTF-8,如此就不會亂碼了。
亂碼問題,這個例子還算是簡單的,僅僅是伺服器應答的時候帶有UTF-8格式的中文字元,從控制檯輸入的字元(手機號碼)是純數字的,沒有涉及到UTF-8編碼問題。如果輸入也帶有中文,那情況會更復雜,有關這方面的詳細情況,可參考我部落格中此前寫的一篇文章「淺談C/C++程式設計中的字元編碼轉換」。
6. gSOAP演練例項:計算器
7. 總結
對本文做個總結:
開發基於SOAP方式的Web Services,不需要自己實現程式碼框架,有諸如gSOAP、Apache CXF這樣的工具會幫我們實現。
以「國內手機號碼歸屬地查詢」為例,重點介紹了gSOAP工具轉換原理,及其使用方法。
- 還遇到了SOAP協議中UTF-8中文字元列印到控制檯會亂碼的問題,並給出瞭解決方法。