一、背景
文章背景来源于十二五课题与西屋的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 state
pIXMLHTTPRequest->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为空字符串。
完