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官方網址:http://www.cs.fsu.edu/~engelen/soap.html
gSOAP開源版下載網址(最新版本):http://sourceforge.net/projects/gsoap2
gSOAP開源版下載網址(歷史版本):https://sourceforge.net/projects/gsoap2/files/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演練實例:計算器
gSOAP官網有提供gSOAP演練實例「Example XML SOAP calculator client (C)」,有興趣的也可以去官網學習下。
7. 總結
對本文做個總結:
開發基於SOAP方式的Web Services,不需要自己實現代碼框架,有諸如gSOAP、Apache CXF這樣的工具會幫我們實現。
以「國內手機號碼歸屬地查詢」爲例,重點介紹了gSOAP工具轉換原理,及其使用方法。
- 還遇到了SOAP協議中UTF-8中文字符打印到控制檯會亂碼的問題,並給出瞭解決方法。