SOAP(Simple Object Access Protocol)是一种协议,用于在网络中交换结构化信息。它被广泛应用于各种Web服务中。本文将介绍如何使用C++实现SOAP客户端与服务端的通信。
SOAP工具包为开发者提供了学习SOAP的良好起点。微软提供的SOAP工具包2.0 Beta 1版本于2000年1月3日发布,它提供了新的示例和帮助文件来解释新的接口。这些示例包括了客户端和服务端的示例,可以帮助开发者构建自己的服务。
然而,这些示例中有一点被忽视了,那就是使用C++来开发服务。所有的示例都是用VB和ASP编写的,这对于使用SOAP的工作来说并不理想。因此,在网上寻找使用VC的SOAP示例,但只找到了一个,而且它使用的是SOAP工具包1.0而不是最新的2.0版本。
由于VB示例运行得很好,决定使用VC开发一个客户端应用程序,使其能够与VB Web服务进行通信。从一个低级别的VB计算器程序和从SOAP工具包1.0中得到的VC示例开始。
为了包含SOAPDLL中的COM接口,使用了#import指令。这用于MSSOAP1.dll,并且添加了一个using子句来使用MSSOAPLib命名空间。VB示例使用了WinInetConnector来连接到Web服务器的服务。这个接口不在MSSOAPLib命名空间中。
也导入了WiSC10.dll,以便有了所需的连接器。这次第二次导入导致了问题。在之前的项目中,从未导入过多个DLL。遇到了一个致命的链接错误:fatal error LNK1179: invalid or corrupt file: duplicate comdat "_IID_ISoapConnector"。经过一些研究,发现必须从第二个导入行中移除named_guids。这解决了链接错误,让其余的编码工作变得更加顺利。
#import "C:\\Program Files\\MSSoapSDK\\Binaries\\MSSOAP1.dll" named_guids raw_interfaces_only exclude("IStream", "ISequentialStream", "_LARGE_INTEGER", "_ULARGE_INTEGER", "tagSTATSTG", "_FILETIME") #import "C:\\Program Files\\MSSoapSDK\\Binaries\\WiSC10.dll" raw_interfaces_only exclude("IStream", "ISequentialStream", "_LARGE_INTEGER", "_ULARGE_INTEGER", "tagSTATSTG", "_FILETIME") using namespace MSSOAPLib;
从这里开始,将VB代码转换为C++接口就变得简单了。这对于以前做过转换的人来说是相当直接的。使用了智能指针来简化对象的创建和释放。
对于这个VC示例,决定使用在网上找到的一个VC示例作为开发的基础。问题是它是一个控制台应用程序,而不是窗口应用程序。决定仍然使用基于控制台的应用程序开发它,并只使用一种类型的命令向服务器发送。对于大多数VC COM开发者来说,将这段代码包含到一个普通的窗口应用程序中应该是一件小事。
要开始这个低级别的SOAP客户端,需要创建一个SoapConnector。SoapConnector的实现可以使用任何类型的传输协议,如HTTP、FTP或SMTP。SOAP文档说SOAP工具包2.0包含了三种不同的SoapConnector实现。这些实现是WinInetConnector、XmlHttpConnector(9x/Me客户端的默认值)和HttpLibConnector(NT/2K客户端的默认值)。选择使用WinInetConnector,因为代码是基于VB示例的。回去尝试了SoapConnector的其他实现,它们在Win2k上都正确地工作了。
// 创建连接器
Connector = NULL;
Connector.CreateInstance(__uuidof(WinInetConnectorLib::WinInetConnector));
// 连接到Web服务
Connector->put_Property(endpoint, vEndPoint);
Connector->Connect(NULL);
// 开始向服务器发送消息
Connector->put_Property(action, vAction);
Connector->BeginMessage(NULL);
创建SoapConnector后,设置了EndPointURL,然后调用了Connect方法。这允许代码连接到Web服务。然后设置SoapAction为uri:Multiply,以告诉服务器将请求的操作。这遵循了工具包中的VB示例。
下一步是创建SoapSerializer。SoapSerializer是允许创建XML消息并将其发送到服务器的接口。
// 创建SoapSerializer
Serializer = NULL;
Serializer.CreateInstance(__uuidof(SoapSerializer));
// 将序列化器连接到连接器的输入流
Connector->get_InputStream(&inputstream);
_variant_t stream = inputstream;
Serializer->Init(stream);
// 手动构建XML消息
Serializer->startEnvelope(NULL, NULL, NULL);
Serializer->startBody(NULL);
Serializer->startElement(method, command, NULL, m);
Serializer->startElement(a, NULL, NULL, NULL);
Serializer->writeString(val1);
Serializer->endElement();
Serializer->startElement(b, NULL, NULL, NULL);
Serializer->writeString(val2);
Serializer->endElement();
Serializer->endElement();
Serializer->endBody();
Serializer->endEnvelope();
// 将消息发送到Web服务
Connector->EndMessage();
创建SoapSerializer后,需要将其附加到SoapConnector的输入流。之后,消息被创建并发送。值得注意的是,当查看源代码时,EndMessage方法是将消息发送到服务器的命令。
到目前为止,已经构建了XML命令并将其发送到服务器执行。下一步是读取结果。在这里省略了错误检查,但可以添加。要读取服务器的回复,客户端应用程序需要使用一个SoapReader。SoapReader连接到SoapConnector的输出流。从那里,可以使用IXMLDOMElement对象读取结果。然后使用std::cout显示。
// 创建SoapReader
Reader = NULL;
Reader.CreateInstance(__uuidof(SoapReader));
// 将读取器连接到连接器的输出流
Connector->get_OutputStream(&outputstream);
_variant_t outstream = outputstream;
VARIANT_BOOL bres;
Reader->load(outstream, &bres);
// 从服务器获取结果并打印
MSSOAPLib::IXMLDOMElement *element;
Reader->get_RPCResult(&element);
BSTR buff;
element->get_baseName(&buff);
std::cout << W2A(buff) << std::endl;
element->get_text(&buff);
std::cout << W2A(buff) << std::endl;
总的来说,SOAP通信对于大多数开发来说都是简单的事情。这里展示的低级通信允许看到XML消息的创建以及解析返回消息。这是一个基于SOAP工具包2.0中的ClcLVBCl示例的简单示例,展示了如何使用VC使用SOAP。