1. 程式人生 > >ONVIF協議網路攝像機(IPC)客戶端程式開發(4):使用gSOAP生成Web Services框架程式碼

ONVIF協議網路攝像機(IPC)客戶端程式開發(4):使用gSOAP生成Web Services框架程式碼

1. 專欄導讀

本專欄第一篇文章「專欄開篇」列出了專欄的完整目錄,按目錄順序閱讀,有助於你的理解,專欄前面文章講過的知識點(或程式碼段),後面文章不會贅述。為了節省篇幅,突出重點,在文章中展示的示例程式碼僅僅是關鍵程式碼,你可以在「專欄開篇」中獲取完整程式碼。

如有錯誤,歡迎你的留言糾正!讓我們共同成長!你的「點贊」「打賞」是對我最大的支援和鼓勵!

2. 不要自己造輪子

ONVIF標準是使用SOAP方式實現的Web Services,本專欄上一篇文章已經介紹了什麼是Web Services,涉及很多概念,包括SOAP、HTTP、XML,RPC等等。辣麼多東東,全部要自己碼程式碼實現嗎?當然不用,我們不必自己造輪子,有現成的工具會幫我們自動生產大部分的程式碼框架。

這樣的工具有很多,比如:

  1. gSOAP工具,適用於C/C++語言開發。
  2. 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到底會自動生成哪些框架程式碼,下圖中淺綠色框中的部分就是自動生成的程式碼。


圖1

4. gSOAP工具轉換原理


圖2

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
  1. 下載gSOAP工具,我的版本是「gsoap_2.8.45」。
  2. 建立一個資料夾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\
    

    最終效果如下:


    圖3
  3. 啟動cmd.exe,確保當前路徑在剛才建立的MobileCode目錄下:


    圖4
  4. 使用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檔案,我這裡是採用線上下載方式。

  5. 使用soapcpp2工具,根據標頭檔案產生框架程式碼,在cmd中執行以下命令:

    soapcpp2.exe -2 -C -c -x -Iimport -Icustom mobilecode.h
    

    -2為生成SOAP 1.2版本的程式碼,-C為僅生成客戶端的程式碼(服務端的不要),-c生成C語言程式碼,詳情可使用soapcpp2.exe -help檢視幫助。

  6. 自動生成的原始碼檔案如下圖所示,


    圖5

    其中custom、import、wsdl2h.exe、soapcpp2.exe、typemap.dat、mobilecode.h、soapClientLib.c這些檔案已經沒用了,可以刪掉,最終剩下的檔案只有:


    圖6

    在soapStub.h檔案中,列出了「國內手機號碼歸屬地查詢」WEB服務的所有介面(Client-Side Call Stub Functions),我們的應用程式通過呼叫這些介面就成了,至於SOAP協議整個過程怎麼實現的,都在soapC.c和soapClient.c中,有興趣的可以去研究,沒興趣的就不管它了,懂得呼叫以下這幾個介面就可以了。


    圖7
  7. 接下來,寫個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;
        }

    第一次執行,如下圖所示,會出現亂碼:


    圖8

    這是由於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,如此就不會亂碼了。


    圖9

    亂碼問題,這個例子還算是簡單的,僅僅是伺服器應答的時候帶有UTF-8格式的中文字元,從控制檯輸入的字元(手機號碼)是純數字的,沒有涉及到UTF-8編碼問題。如果輸入也帶有中文,那情況會更復雜,有關這方面的詳細情況,可參考我部落格中此前寫的一篇文章「淺談C/C++程式設計中的字元編碼轉換」。

6. gSOAP演練例項:計算器

7. 總結

對本文做個總結:

  1. 開發基於SOAP方式的Web Services,不需要自己實現程式碼框架,有諸如gSOAP、Apache CXF這樣的工具會幫我們實現。


    圖10
  2. 以「國內手機號碼歸屬地查詢」為例,重點介紹了gSOAP工具轉換原理,及其使用方法。

  3. 還遇到了SOAP協議中UTF-8中文字元列印到控制檯會亂碼的問題,並給出瞭解決方法。