正在看着AV(AreCont)SDK的时候,老大突然让我转到soap上,原因是刚进公司的我,第一项任务便是研究ONVIF(so many specifications),当然主要的还是看core specification,这是一个axis,bosch and sony 三大牛x整出来的标准规范,为开发网络视频设备软件开发商提供一个标准规范,但是至今为止仍只是少数公司加入了标准规范的行列,可能是他们不求上进吧 。
在core specification中最主要的还是提到了webservice,兼容RTSP,RTCP, RTP 实现实时流传输,采用vc++实现,所以公司还是考虑通过vc++调用webservice,所以老大便给了我这个任务,先熟悉一下。整了一天,才搞了个差不多,感觉很是纳闷,其实程序看起来很简单,但是细节往往能让人迷糊一大段时间。
其实看了阿瑞得文章后,基本上差不多,当然先装soap toolkit3.0是前提,不过中间还是遇到很多问题的,又通过不断的查阅才得以解决,其中总是发生中断的情况,这个主要是因为webservice得URL没有写正确或者没有搞清URL和Namespace Uri的原因吧。不多说了,还是先看我做的流程吧。
First step: New a Asp.net Web Service Application project.
The following is the content of Service1.asmx.cs:
using System;
using System.Linq;
using System.Web;
using System.Web.Services;
using System.Web.Services.Protocols;
using System.Xml.Linq;
namespace TestWebService
{
[SoapDocumentService(RoutingStyle = SoapServiceRoutingStyle.RequestElement)]
[WebService(Namespace = "http://localhost/WebService/")]
[WebServiceBinding(ConformsTo = WsiProfiles.BasicProfile1_1)]
// 若要允许使用 ASP.NET AJAX 从脚本中调用此 Web 服务,请取消对下行的注释。
// [System.Web.Script.Services.ScriptService]
public class Service : System.Web.Services.WebService
{
public Service()
{
//CODEGEN: 该调用是 ASP.NET Web 服务设计器所必需的
InitializeComponent();
}
#region 组件设计器生成的代码
//Web 服务设计器所必需的
private System.ComponentModel.IContainer components = null;
/**/
///
/// 设计器支持所需的方法 - 不要使用代码编辑器修改
/// 此方法的内容。
///
private void InitializeComponent()
{
}
/**/
///
/// 清理所有正在使用的资源。
///
protected override void Dispose(bool disposing)
{
if (disposing && components != null)
{
components.Dispose();
}
base.Dispose(disposing);
}
#endregion
// WEB 服务示例
// HelloWorld() 示例服务返回字符串 Hello World
// 若要生成,请取消注释下列行,然后保存并生成项目
// 若要测试此 Web 服务,请按 F5 键
[WebMethod]
public int Hello(int strName, int strPwd)
{
int temp = strName * strPwd;
return temp;
}
[WebMethod]
public string HelloWorld(int strName, int strPwd)
{
return "我是shawn!";
}
[WebMethod]
public string GetStringName()
{
return "shawn,rock!!!";
}
}
}
Then F5 debug mode test the application.
Second step:
New a win32 console application:
New a WSWrapper class:
WSWrapper.h
#ifndef _WS_WRAPPER_H_
#define _WS_WRAPPER_H_
#import "msxml4.dll"
#import "C:/Program Files/Common Files/MSSoap/Binaries/mssoap30.dll" /
exclude("IStream", "IErrorInfo", "ISequentialStream", "_LARGE_INTEGER", /
"_ULARGE_INTEGER", "tagSTATSTG", "_FILETIME")
#include
#include
using namespace MSXML2;
using namespace MSSOAPLib30;
using std::string;
typedef struct
{
char * sParamName;
char * sParamValue;
}WSPARAM;
class WSWrapper
{
public:
WSWrapper();
virtual ~WSWrapper();
bool InvokeSoap(string wsURL,
string wsNameSpace,
string wsMethod,
WSPARAM *wsParam,
int wsCount,
char *sEmsg,
char *sRmsg,
bool wsXML);
private:
/* const char *_wsURL;
const char * _wsNameSapce;
const char *_wsMethodName; */
};
#endif
WSWrapper.cpp
#include"WSWrapper.h"
#include
#include
using namespace std;
WSWrapper::WSWrapper()
{
}
WSWrapper::~WSWrapper()
{
try
{
CoUninitialize();
}
catch(...)
{
}
}
// 参数说明:
// wsURL:WEBSERVICE的链接地址
// wsNameSpace:在页面中定义的命名空间,一般地使用:http://tempuri.org/
// wsMethod:要访问的WEBSERVICE 方法名
// wsParam: 要使用的参数,见WSPARAM定义,
// wsCount: 参数总数
// sEmsg://要返回给调用函数的错误码率信息
// sRmsg: //要返回的捃执行结果信息
// wsXML:要返回的是XML值还是TEXT
// 返回值:
// 如果正常执行返回true,中间有问题就返回false;
bool WSWrapper::InvokeSoap(string wsURL,
string wsNameSpace,
string wsMethod,
WSPARAM *wsParam,
int wsCount,
char *sEmsg,
char *sRmsg,
bool wsXML)
{
HRESULT hr = CoInitialize(NULL);//初始化com环境
if(FAILED(hr))
{
strcpy(sEmsg,"初始化COM失败");
printf("initialized com failed.");
return false;
}
ISoapSerializerPtr Serializer;
ISoapReaderPtr Reader;
ISoapConnectorPtr Connector;
//连接到WebService
hr = Connector.CreateInstance(__uuidof(HttpConnector30));
if(FAILED(hr))
{
//创建com对象出错,一般是因为没有安装com
strcpy(sEmsg,"创建HttpConnector FAILED");
printf("create HttpConnector failed.");
return false;
}
Connector->Property["EndPointURL"] = wsURL.c_str();
hr = Connector->Connect();
if (FAILED(hr))
{
strcpy(sEmsg,"连接WEBSERVICE FAILED");
printf("connecting webservice failed.");
return false;
}
Connector->Property["SoapAction"] = (wsNameSpace + wsMethod).c_str();
//开始创建webservice的请求Soap包
Connector->BeginMessage();
hr = Serializer.CreateInstance(__uuidof(SoapSerializer30));
if (FAILED(hr))
{
strcpy(sEmsg,"CreateInstance Serializer FAILED");
printf("create instance Serializer failed.");
return false;
}
Serializer->Init(_variant_t((IUnknown*)Connector->InputStream));
Serializer->StartEnvelope("SOAP", "http://schemas.xmlsoap.org/soap/envelope/", "");
Serializer->SoapAttribute("xsi", "", "http://www.w3.org/2001/XMLSchema-instance", "xmlns");
Serializer->SoapAttribute("xsd", "", "http://www.w3.org/2001/XMLSchema", "xmlns");
//Serializer->StartEnvelope("","","");
Serializer->StartBody(L"NONE");
Serializer->StartElement(wsMethod.c_str(), wsNameSpace.c_str(), "NONE", "");
for (int i = 0; i < wsCount; i++)
{
Serializer->StartElement(wsParam[i].sParamName,wsNameSpace.c_str(),"NONE","");
Serializer->WriteString(wsParam[i].sParamValue);
Serializer->EndElement();
}
Serializer->EndElement();
Serializer->EndBody();
Serializer->EndEnvelope();
try
{
Connector->EndMessage();
}
catch (...)
{
strcpy(sEmsg,"提交SOAP消息出错");
printf("提交SOAP消息出错");
return false;
}
//解析返回的soap包
hr = Reader.CreateInstance(__uuidof(SoapReader30));
if(FAILED(hr))
{
strcpy(sEmsg,"CreateInstance READER FAILED");
printf("create instance reader failed.");
return false;
}
Reader->Load(_variant_t((IUnknown*)Connector->OutputStream), "");
if (wsXML)
{
strcpy(sRmsg,(const char*)Reader->RpcResult->xml);
}
else
strcpy(sRmsg,(const char*)Reader->RpcResult->text);
return true;
}
The last file is TestApp.cpp:
#include "WSWrapper.h"
int main()
{
string url = "http://localhost:1451/Service1.asmx";
string uri = "http://localhost/WebService/";
string methodName = "HelloWorld";
WSPARAM *params = new WSPARAM[2];
params[0].sParamName = "strName";
params[0].sParamValue = "3";
params[1].sParamName = "strPwd";
params[1].sParamValue = "4";
char errorMessage[20];
char revMessage[100];
WSWrapper wsWrapper;
int wsParaCount = 2;
bool retValue = wsWrapper.InvokeSoap(url,uri,methodName,params,wsParaCount,errorMessage,revMessage,false);
if (retValue)
{
printf("%s",revMessage);
}
getchar();
getchar();
return 0;
}
其实我遇到的一个最大的问题就是没有返回值的问题,最终还是不小心找到了解决方案,就是
Serializer->StartElement("strName", _wsNameSapce.c_str(), "NONE", "");
//Serializer->SoapAttribute("xsi:type", "", "xsd:string", "");
Serializer->WriteString("3");
Serializer->EndElement();
Serializer->StartElement("strPwd",_wsNameSapce.c_str(),"NONE","");
Serializer->WriteString("4");
Serializer->EndElement();
一开始的时候,并没有指定strName和strPwd的namespace,最好是跟method的namespace是一致的,就是_wsNameSapce.c_str()。还有一个就是注意当运行webservice的时候把TestApp.cpp中的url改成运行测试webservice的url路径。当然还遇到了其他很多方面的问题,欢迎有需要的人跟我交流。