一、背景
文章背景來源於十二五課題與西屋的SystematICS綜合監控的介面問題,其中History Recorder服務部署為對外資料發布的介面。History Recorder服務允許SystematICS服務物件資料和列表資料配置成儲存並轉寄給外系統處理軟體。外系統處理軟體需要在History Recorder中配置,這樣綜合監視軟體可以監視它的串連狀態。可以使用檔案或者組態工具配置History Recorder服務,需要配置的資訊主要有:
• 表示資料要發送到的應用程式的路徑;
• 所要發送給應用程式的資料;
• 所要發送給應用程式的表格。
History Recorder服務使用HTTP協議向外系統發送資料,ISCS將根據配置主動串連到外系統的web服務,首次串連後將發送全部資料,之後將發送變化資料並定期更新全部資料。外系統的web服務需至少響應前3個http訊息(回應200 OK)。
發送裝置狀態(資料點)的格式如下:
---------------http頭------------------
POST / HTTP/1.1
host:192.10.36.36:808
content-location: oil://scada.TMS/hpx_prc_do_0000
content-type: application/calendar+xml
content-length:111
---------------data區------------------
<vjournal>
<tags>bad</tags>
<dtstart>2009-02-25T10:01:19.933723Z</dtstart>
<description>1</description>
</vjournal>
---------------結束------------------
發送資料時是以XML包封裝內容,基於這些情況描述,西屋的對外介面類似SOAP協議。
二、Lacewing
這篇文章主要講述怎樣把Web Server整合到你的Win32 C++應用中。使用lacewing網路程式庫編寫的C++ Web應用程式具有更快的速度和良好的伸縮性,佔用系統資源極低。liblacewing 是一個跨平台的進階網路程式庫,用於C和C++。旨在提供一組直觀的Socket通訊類,側重於穩定性和平台最佳化(支援IOCP, epoll 和kqueue)。
Lacewing::Webserver 作為一個功能強大的HTTP server, 主要有以下特徵:
•HTTP GET/POST with automatic parameter parsing
•Non-blocking file sending
•Cookies and sessions
•Multipart file upload
•Getting/setting the last modified date, to easily implement caching
•Full HTTPS support
Lacewing原始碼:http://lacewing-project.org/
三、測試環境
1、 作業系統
WINDOWS 7
2、系統
處理器:Intel(R) Core(TM) i5-2401M CPU @ 2.30GH 2.30GH
安裝記憶體:3.00GB (2.88 GB可用)
系統類別型:32位作業系統
3、工具環境
A、vc++6.0
B、VS2008
四、建立Web Server Project
下載Lacewing原始碼,使用VS2008編譯之後會在bin目錄下產生lacewind.dll和lacewing.lib。
使用VS2008建立一個testWebServer WIN32控制台工程。同時,把lacewind.dll和lacewing.lib複製到工程檔案目錄下,在工程屬性中配置包含lacewing.lib檔案。
// testWebServer.cpp : Defines the entry point for the console application.//#include "lacewing.h"#include <assert.h>#include <string.h>#import <msxml4.dll>using namespace MSXML2;void PaserXml(BSTR bstXML){ MSXML2::IXMLDOMDocumentPtr pDoc; HRESULT hr;hr = pDoc.CreateInstance(__uuidof(MSXML2::DOMDocument40)); if (FAILED(hr)) { printf("無法建立DOMDocument對象,請檢查是否安裝了MS XML Parser 運行庫!"); return ; } try { // 負載檔案// pDoc->load("test.xml");pDoc->loadXML(bstXML);long lChilds, i;// 根節點MSXML2::IXMLDOMElementPtr pRootElement = pDoc->GetdocumentElement();printf("root = %s\n", (char *)pRootElement->GetnodeName());// 根節點的一級子節點MSXML2::IXMLDOMNodeListPtr pNodeList = pRootElement->GetchildNodes();lChilds = pNodeList->Getlength();char szNodeName[48], szValue[64];for (i = 0; i < lChilds; i++){MSXML2::IXMLDOMNodePtr pNode = pNodeList->Getitem(i);// 過濾注釋節點if (pNode->GetnodeType() != MSXML2::NODE_COMMENT){BSTR bstrNodeName = pNode->GetnodeName();char *lpszNodeName = _com_util::ConvertBSTRToString(bstrNodeName);strncpy(szNodeName, lpszNodeName, sizeof(szNodeName));delete []lpszNodeName;BSTR bstrValue = pNode->Gettext();char *lpszValue = _com_util::ConvertBSTRToString(bstrValue);strncpy(szValue, lpszValue, sizeof(szValue));delete []lpszValue;printf("%s:%s\n", szNodeName, szValue);}}}catch (_com_error &e) {printf("Description = '%s'\n", (char*)e.Description()); if (pDoc){ pDoc.Release();} }}void onGet(Lacewing::Webserver &, Lacewing::Webserver::Request &Request){ /* The MIME type defaults to "text/html" */ Request.SetMimeType("text/html"); Request.WriteFile("post.html");}Void onPost(Lacewing::Webserver &Webserver, Lacewing::Webserver::Request &Request){// 這裡szBody將顯示HttpPost發送的XML格式化資料//<vjournal>// <tags>bad</tags>// <dtstart>2009-02-25T10:01:19.933723Z</dtstart>// <description>1</description>//</vjournal>char *szBody = (char *)Request.Body();BSTR bstrBody = _com_util::ConvertStringToBSTR(szBody);PaserXml(bstrBody);::SysFreeString(bstrBody);Request << "Hello SystematICS!";struct Lacewing::Webserver::Request::Header * Header = NULL; for (Header = Request.FirstHeader(); Header; Header = Header->Next()) { const char *szName = Header->Name();const char *szValue = Header->Value(); } Lacewing::Webserver::Request::Parameter *p = NULL; for (p = Request.POST(); p; p = p->Next()) {const char *szName = p->Name();const char *szValue = p->Value(); }}int main(int argc, char * argv[]){ CoInitialize(NULL); Lacewing::EventPump EventPump; Lacewing::Webserver Webserver(EventPump); Webserver.onGet(onGet); Webserver.onPost(onPost); Webserver.Host(8080); EventPump.StartEventLoop(); CoUninitialize(); return 0;}
五、建立HTTP Post Project
使用VS6建立一個testHttpPost WIN32控制台工程。
// testHttpPost.cpp : Defines the entry point for the console application.//#include "stdafx.h"#include <string>#include <iostream>#import <msxml4.dll>using namespace MSXML2;std::wstring GenerateData(){std::wstring wvarQuery;IXMLDOMDocumentPtr pDoc;IXMLDOMElementPtr xmlRoot;// 建立DOMDocument對象HRESULT hr = pDoc.CreateInstance(__uuidof(DOMDocument40));if (!SUCCEEDED(hr)){ printf("無法建立DOMDocument對象,請檢查是否安裝了MS XML Parser運行庫!");return wvarQuery;}// 根節點的名稱為vjournal// 建立元素並添加到文檔中xmlRoot = pDoc->createElement("vjournal");pDoc->appendChild(xmlRoot);IXMLDOMElementPtr pNode;// 添加“tags”元素pNode = pDoc->createElement("tags");pNode->Puttext((const char *)"bad");xmlRoot->appendChild(pNode);// 添加“dtstart”元素pNode = pDoc->createElement("dtstart");pNode->Puttext((const char *)"2009-02-25T10:01:19.933723Z");xmlRoot->appendChild(pNode);// 添加“description”元素pNode = pDoc->createElement("description");pNode->Puttext((const char *)"1");xmlRoot->appendChild(pNode);// 儲存到檔案,如果存在就覆蓋,否則不存在就建立.// pDoc->save("test.xml");wvarQuery = pDoc->Getxml(); return wvarQuery;}void PaserXml(BSTR bstXML){ IXMLDOMDocumentPtr pDoc; HRESULT hr; hr = pDoc.CreateInstance(__uuidof(DOMDocument40)); if (FAILED(hr)) { printf("無法建立DOMDocument對象,請檢查是否安裝了MS XML Parser 運行庫!"); return ; } // 負載檔案 // pDoc->load("test.xml"); // 載入記憶體 pDoc->loadXML(bstXML);long lChilds, i;// 根節點IXMLDOMElementPtr pRootElement = pDoc->GetdocumentElement(); printf("root = %s\n", (char *)pRootElement->GetnodeName()); // 根節點的一級子節點 IXMLDOMNodeListPtr pNodeList = pRootElement->GetchildNodes(); lChilds = pNodeList->Getlength(); char szNodeName[48], szValue[64]; for (i = 0; i < lChilds; i++) { IXMLDOMNodePtr pNode = pNodeList->Getitem(i);// 過濾注釋節點if (pNode->GetnodeType() != NODE_COMMENT){BSTR bstrNodeName = pNode->GetnodeName();char *lpszNodeName = _com_util::ConvertBSTRToString(bstrNodeName);strncpy(szNodeName, lpszNodeName, sizeof(szNodeName));delete []lpszNodeName;BSTR bstrValue = pNode->Gettext();char *lpszValue = _com_util::ConvertBSTRToString(bstrValue);strncpy(szValue, lpszValue, sizeof(szValue));delete []lpszValue;printf("%s:%s\n", szNodeName, szValue); } }}void SendData(std::wstring url){ long lState, lStatus; HRESULT hr; IXMLHTTPRequestPtr pIXMLHTTPRequest; IXMLDOMDocumentPtr pXMLDoc; try { hr = pIXMLHTTPRequest.CreateInstance(__uuidof(XMLHTTP)); SUCCEEDED(hr) ? 0 : throw hr; hr = pIXMLHTTPRequest->open("POST", url.c_str(), false); SUCCEEDED(hr) ? 0 : throw hr; // 如果要向伺服器post資料,這個地方一定要設定為application/x-www-form-urlencoded pIXMLHTTPRequest->setRequestHeader("Content-Type", "application/x-www-form-urlencoded");std::wstring wvarQuery = GenerateData();_variant_t vartQueryFields(wvarQuery.c_str());PaserXml(vartQueryFields.bstrVal);hr = pIXMLHTTPRequest->send(vartQueryFields); SUCCEEDED(hr) ? 0 : throw hr; // 處理返回的xml資料,對返回的xml資料進行解析,主要是dom方法. pXMLDoc = pIXMLHTTPRequest->responseXML;// Retrieve the statepIXMLHTTPRequest->get_readyState(&lState);if (lState == 4){// The request has completed.// Get the request status.pIXMLHTTPRequest->get_status(&lStatus);if (lStatus == 200){// Get the response body if we were successful.BSTR bstrbody;pIXMLHTTPRequest->get_responseText(&bstrbody);char *lpszBody = _com_util::ConvertBSTRToString(bstrbody);// 這裡將顯示WebServer body區的資料“Hello SystematICS!”delete []lpszBody;} } } catch (_com_error &e) {printf("Description = '%s'\n", (char*)e.Description()); if (pIXMLHTTPRequest){pIXMLHTTPRequest.Release();} if (pXMLDoc){ pXMLDoc.Release();} }}int main(int argc, char* argv[]){CoInitialize(NULL); SendData(L"http://localhost:8080");CoUninitialize(); return 0;}
六、測試
1)、HTTP CLIENT發送:
POST / HTTP/1.1
Accept: */*
Accept-Language: zh-cn
Content-Type: application/x-www-form-urlencoded
Accept-Encoding: gzip, deflate
User-Agent: Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 6.1; Trident/4.0; SLCC2; .NET CLR 2.0.50727; .NET CLR 3.5.30729; .NET CLR 3.0.30729; Media Center PC 6.0; .NET4.0C; .NET4.0E)
Host: localhost:8080
Content-Length: 113
Connection: Keep-Alive
Cache-Control: no-cache
<vjournal><tags>bad</tags><dtstart>2009-02-25T10:01:19.933723Z</dtstart><description>1</description></vjournal>
注意:文本最後是“\r\n\r\n”兩個斷行符號換行結束。
2)、HTTP SERVER接收:
POST / HTTP/1.1
Accept: */*
Accept-Language: zh-cn
Content-Type: application/x-www-form-urlencoded
Accept-Encoding: gzip, deflate
User-Agent: Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 6.1; Trident/4.0; SLCC2; .NET CLR 2.0.50727; .NET CLR 3.5.30729; .NET CLR 3.0.30729; Media Center PC 6.0; .NET4.0C; .NET4.0E)
Host: localhost:8080
Content-Length: 113
Connection: Keep-Alive
Cache-Control: no-cache
<vjournal><tags>bad</tags><dtstart>2009-02-25T10:01:19.933723Z</dtstart><description>1</description></vjournal>
注意:文本最後是“\r\n\r\n”兩個斷行符號換行結束。
3)、HTTP SERVER響應:
"HTTP/1.1 200 OK\r\nContent-Length:0\r\n\r\n"
即:
HTTP/1.1 200 OK
Content-Length:0
注意:文本最後是“\r\n\r\n”兩個斷行符號換行結束。長度為0表示BODY為空白字串。
完