C++ 編寫WebService服務
轉自https://blog.csdn.net/lixiang987654321/article/details/50932256 原文很好,稍作修改
在網路上已經以Webserice形式提供了很多型別的介面,例如獲取網路時間、獲取天氣、獲取手機歸屬地等等,可見webservice應用已經非常廣泛,特別是在網路發展飛速的今天,它是一個跨平臺的、可擴充套件的RPC應用,所以學習它非常實用,說不定哪天你就需要將服務其功能轉為webservice介面對接其他系統或平臺,這時,這技術就用的上了。
GSoap是一個開源的工具,功能非常強大,各位可以到網路上搜索學習看看這個不錯的工具,gSOAP編譯工具提供了一個SOAP/XML關於C/C++語言的實現,從而讓C/C++語言開發web服務或客戶端程式的工作變得輕鬆了很多。絕大多數的C++web服務工具包提供一組API函式類庫來處理特定的SOAP資料結構,這樣就使得使用者必須改變程式結構來適應相關的類庫。這裡我主要通過C/S模式呼叫GSOAP來實現服務端和客戶端。首先我的目標是提供一組簡單數學的介面:加減乘除四個介面。
在使用GSOAP提供幾個介面之前,首先帶大家熟悉一下Windows下GSOAP裡面攜帶的兩個工具:wsdl2h.exe以及soapcpp2.exe,兩工具可以在下載的gsoap2.8.29(下載可以到開源社群去下載:http://sourceforge.net/projects/gsoap2)的gsoap/bin/win32目錄找到,當然這是gsoap在windows提供的兩個可直接的工具,下面我們先來認識一下這兩個簡單易用的工具:
wsdl2h工具: 正如其名wsdl到h,也就是webservice description language(.wsdl檔案)轉換成.h的標頭檔案工具,換句話說就是用來轉為.h的工具,這裡我們不介紹wsdl檔案如何編寫,也不介紹wsdl轉h功能,因為我們自己已經寫好的.h檔案,所以用不到,如果想了解,請到onvif官網下載相關wsdl然後用這個工具試試!具體功能如下:
(1)、它僅僅負責生成wsdl中描述的相應的標頭檔案,這些標頭檔案不能直接使用,需要經過soapcpp2.exe轉換為gsoap生成的介面後才能使用
(2)、輸入條件為一個或多個wsdl或xsd檔案或者相應的URL路徑,如果輸入wsdl或xsd則輸出為第一個名稱的.h檔案,如果為URL則預設為標準輸出
攜帶引數:
-s: don't generate STL code 也就是不使用標準模板庫,如果不使用-s引數那麼預設使用STL庫,也就是必須將以來的檔案./gsoap/stdsoap2.h和stdsoap2.cpp拷貝至應用的程式當中。
-c: generate C source code c意思是生成c檔案介面,預設是c++標頭檔案
-o:output to file 指定輸出檔名稱,可以重新命名生成的檔名稱
-t:use type map file instead of the default file typemap.dat 使用型別定義檔案,可以在typemap.dat(自帶的)中修改生成選項,生成符合要求的標頭檔案,如編碼
當然,該用具還有其他n個引數使用,此處僅僅介紹幾個常用的,其他控制引數自己去了解學習。
soapcpp2.exe:該工具功能是將我們提供或有wsdl2h生成的.h的標頭檔案(介面)轉換為gsoap提供的形式的介面和rpc代理相關的程式碼和框架(當然我沒有具體去了解,vs中你可以搜尋到攜帶相應介面名的其他代理方法並且有相應的實現)
相關引數如下:
(1)、-1生成SOAP1.1協議的程式碼
(2)、-2生成SOAP1.2協議的程式碼
(3)、-C只是生成客戶端相關的程式碼
(4)、-S只是生成服務端相關的程式碼
(5)、-i generate C++ service proxies and objects inherited from soap struct 生成物件為soap的子類,會影響生成檔案的使用方法。如 class SOAP_CMAC BasicCMIRPService : public soap
其他引數此處不再介紹,如需要更詳細解釋,請到官網查詢相應工具使用介紹。
好,學習兩個工具之後,我們就可以將以上工具派上用場了,當然我們這裡不需要wsdl2h工具,因為我們是C++程式設計師,標頭檔案我們自己編寫提供即可,不需要編寫wsdl在轉為.h檔案,省去了不少麻煩,我們直接使用soapcpp2工具將標頭檔案轉對應的頭即可,首先服務端提供加減乘除4個介面,客戶端呼叫加減乘除4個介面,約定介面如下:
int Add(int nNum1, int nNum2, int* pResult);
int Sub(int nNum1, int nNum2, int* pResult);
int Mul(int nNum1, int nNum2, int* pResult);
int Div(int nNum1, int nNum2, int* pResult);
返回值我們以引數地址形式提供出去而不是以函式返回值形式提供跟gsoap生成介面有關,它生成介面呼叫的時候返回值表示傳入引數是否正確或呼叫成功用到了返回值,所以我們自己的介面為了不覆蓋它的使用得這麼寫,當然,形式變化而已,無所謂!既然客戶端和伺服器都以改4介面為通訊介面,那麼這4個介面就作為我們要轉換為gsoap代理或框架時soapcpp2用到的標頭檔案的宣告,新建服務端程式,新建win32工程專案:GSoapServer空專案,新增類GSoapServer,然後刪除該類所有宣告和實現(目的就是用GSaopServer.h和GSaopServer.cpp兩個配對檔案,不需要相應的類),在GSoapServer.h中宣告以上幾個函式:
#pragma once
int Add(int nNum1, int nNum2, int* pResult);
int Sub(int nNum1, int nNum2, int* pResult);
int Mul(int nNum1, int nNum2, int* pResult);
int Div(int nNum1, int nNum2, int* pResult);
在GSoapServer.cpp中新增main函式
#include "GSoapServer.h"
int main(int argc, char** argv)
{
return 0;
}
編譯該專案,保證編譯通過無錯誤,接下來見gsoap2.8工具目錄下的./gsoap/bin/win32/gsoapcpp2.exe 拷貝至與GSoapServer.h相同目錄下,使用命令列cmd,進入該目錄
如:
鍵入:cd E:/project/C++/test/GSoapServer/GSoapServer
鍵入:E:
使用soapcpp2.exe: soapcpp2.exe GSoapServer.h
此時,由於我們沒有指定-C或-S,它會為我們生成客戶端和服務端的所有程式碼,如果只是生成客戶端程式碼新增-C引數,此時會生成soapH.c、soapC.cpp、soapStub.h、soapClient.cpp soapClientLib.cpp程式碼(加上我們以來的stl庫stdsoap2.h和stdsoap2.cpp供7個檔案,當然包含被轉的GSoapServer.h檔案就是8個,此處生成後要不要GSaopServer.h已經無所謂了);如果只想生成服務端的程式碼,那麼新增-S引數,此時會生成soapH.c、soapC.cpp、soapStub.h、soapServer.cpp soapServerLib.cpp以及soap.nsmap檔案,也就是8個檔案(不計算GsopServer.h被轉換的標頭檔案)
其中soap.h和soapC.cpp是具體的實現,soapStub.h為代理的宣告(服務端我們需要實現宣告的4個介面-該四個介面和我們約定的不太一樣,第一個引數多了soap*物件指標)
對於服務端,我們將soap.h soapC.cpp soapStub.h soapServer.cpp以及依賴的stl庫stdsoap2.h和stdsoap2.cpp 五個檔案新增的專案中(soapServerLib.cpp不需要,否則會出錯),新增後所有的cpp屬性設定預編譯頭為“不使用預編譯頭”,最後找到soapStub.h檔案最後幾行會發現我們約定的即可介面宣告(介面稍微有變化,多了soap*引數),拷貝到GSoapServer.cpp工程目錄的main函式之前,並且實現之:
/** Web service operation 'Add' (returns SOAP_OK or error code) */
SOAP_FMAC5 int SOAP_FMAC6 Add(struct soap*, int nNum1, int nNum2, int *pResult)
{
*pResult = nNum1 + nNum2;
return SOAP_OK;
}
/** Web service operation 'Sub' (returns SOAP_OK or error code) */
SOAP_FMAC5 int SOAP_FMAC6 Sub(struct soap*, int nNum1, int nNum2, int *pResult)
{
*pResult = nNum1 - nNum2;
return SOAP_OK;
}
/** Web service operation 'Mul' (returns SOAP_OK or error code) */
SOAP_FMAC5 int SOAP_FMAC6 Mul(struct soap*, int nNum1, int nNum2, int *pResult)
{
*pResult = nNum1 * nNum2;
return SOAP_OK;
}
/** Web service operation 'Div' (returns SOAP_OK or error code) */
SOAP_FMAC5 int SOAP_FMAC6 Div(struct soap*, int nNum1, int nNum2, int *pResult)
{
if(0 == nNum2)
{
*pResult = 0;
return -1;
}
*pResult = nNum1 / nNum2;
return SOAP_OK;
}
編譯,發現沒有錯誤,如果有預編譯錯誤,設定cpp屬性去掉預編譯頭即可,切記不需要soapServerLib.cpp生成的檔案,soapServer.cpp是必要的,它實現了soap_serve服務,也就是我們伺服器提供的服務!
具體的伺服器main函式實現如下:
#include "GSoapServer.h"
#include “soap.nsmap” // 服務端與客戶端必須包含的檔案
/** Web service operation 'Add' (returns SOAP_OK or error code) */
SOAP_FMAC5 int SOAP_FMAC6 Add(struct soap*, int nNum1, int nNum2, int *pResult)
{
*pResult = nNum1 + nNum2;
return SOAP_OK;
}
/** Web service operation 'Sub' (returns SOAP_OK or error code) */
SOAP_FMAC5 int SOAP_FMAC6 Sub(struct soap*, int nNum1, int nNum2, int *pResult)
{
*pResult = nNum1 - nNum2;
return SOAP_OK;
}
/** Web service operation 'Mul' (returns SOAP_OK or error code) */
SOAP_FMAC5 int SOAP_FMAC6 Mul(struct soap*, int nNum1, int nNum2, int *pResult)
{
*pResult = nNum1 * nNum2;
return SOAP_OK;
}
/** Web service operation 'Div' (returns SOAP_OK or error code) */
SOAP_FMAC5 int SOAP_FMAC6 Div(struct soap*, int nNum1, int nNum2, int *pResult)
{
if(0 == nNum2)
{
*pResult = 0;
return -1;
}
*pResult = nNum1 / nNum2;
return SOAP_OK;
}
int main(int argc, char**argv)
{
struct soap math_service;
soap_init(&math_service);
// bind埠返回SOCKET套接字-雷同socket套介面函式伺服器監聽過程
if(soap_bind(&math_service, NULL, 8686, 100))
{
soap_print_fault(&math_service, stderr);
return -1;
}
fprintf(stderr, "start math webservice ...\n");
// 當然這裡可以用多個soap在多個現成中使用soap_new建立新的soap,每個執行緒一個
while(true)
{
int nClient = (int)soap_accept(&math_service);
if(nClient < 0)
{
soap_print_fault(&math_service, stderr);
return -1;
}
fprintf(stderr, "client[%d] connect success. ..\n", nClient);
soap_serve(&math_service);// provide service
soap_end(&math_service);// end service
}
return 0;
}
啟動服務端程式,開啟IE輸入:http://localhost:8686/
會發現IE返回:
<SOAP-ENV:Fault xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/" xmlns:SOAP-ENC="http://schemas.xmlsoap.org/soap/encoding/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<faultcode>at source</faultcode>
<faultstring>HTTP GET method not implemented</faultstring>
</SOAP-ENV:Fault>
說明,服務端啟動成功!
截圖如下:
接下來寫客戶端程式:
客戶端很簡單,因為所有的檔案我們已經用soapcpp2.exe工具生成了,新建GSoapClient工程,空工程或預設的win32控制檯都可以,將服務端生成的檔案soapH.h soapStub.h stdsoap2.h以及對應的原始檔soapC.cpp soapClient.cpp stdsoap2.cpp 拷貝至客戶端工程目錄下並新增檔案至專案中,然後設定cpp對應的屬性,去掉預編譯標頭檔案,包含soap.nsmap檔案,實現並呼叫webservice介面如下:
// GSoapClient.cpp : 定義控制檯應用程式的入口點。
//
#include "stdafx.h"
#include <stdlib.h>
#include "soap.nsmap"
int _tmain(int argc, _TCHAR* argv[])
{
struct soap math_soap;
soap_init(&math_soap);
int nResult = 0;
const char* pWebService = "http://127.0.0.1:8686";
soap_call_Add(&math_soap, pWebService, "", 60, 30, &nResult);
if (math_soap.error)
{
soap_print_fault(&math_soap, stderr);
}
printf("webservice:60+30=%d\n", nResult);
soap_call_Sub(&math_soap, pWebService, "", 60, 30, &nResult);
if (math_soap.error)
{
soap_print_fault(&math_soap, stderr);
}
printf("webservice:60-30=%d\n", nResult);
soap_call_Mul(&math_soap, pWebService, "", 60, 30, &nResult);
if (math_soap.error)
{
soap_print_fault(&math_soap, stderr);
}
printf("webservice:60*30=%d\n", nResult);
soap_call_Div(&math_soap, pWebService, "", 60, 30, &nResult);
if (math_soap.error)
{
soap_print_fault(&math_soap, stderr);
}
printf("webservice:60/30=%d\n", nResult);
soap_end(&math_soap);
soap_done(&math_soap);
system("pause");
return 0;
}
工程檔案列表如圖下:
標頭檔案包含注意:
編譯生成客戶端程式:
啟動服務端:
啟動客戶端列印結果:
服務端列印(說明每一次webservice都對應一個連線不是?呵呵):
以上就是C++中使用gsoap工具完成webservice服務整個服務端以及客戶端的實現過程,比較簡單,這個功能簡單使用,研究了很久,網路上很多文章寫的有點亂,很難總結,總覺得要寫一篇簡單易懂,根據我的操作不會錯誤的文章,花了大概2-3個小時看資料、寫Demon、截圖寫文章等等,雖然比較費時間,但想想,我能為各位網友出一份力,為網際網路技術出一份力,苦了累了到無所謂了,感謝大家的支援,如果需要程式碼,記得給我送上分(作為我為大家的付出的回報,行不,下載後可以返回的,分數不會少,對你對我都有益,行動起來吧!謝謝觀看~)
使用的gsoap2.8.29連線下載:http://download.csdn.net/detail/lixiang987654321/9466782
C++寫的webservice介面Demon下載:http://download.csdn.net/detail/lixiang987654321/9466781