gSoap编写代码访问WCF服务

来源:互联网 发布:淘宝商城商业模式 编辑:程序博客网 时间:2024/05/24 01:52


最近给人写个gSoap的C++程序访问WCF服务,结果调用的时候没有返回0(SOAP_OK),而是415。无奈,无法直接在别人的机器上进行调试,那就自己搭一个简单的WCF服务吧。


测试用的WCF服务就是根据Visual Studio的向导生成的代码。


using System;using System.Collections.Generic;using System.Linq;using System.Runtime.Serialization;using System.ServiceModel;using System.ServiceModel.Web;using System.Text;namespace SimpleServer{    // NOTE: You can use the "Rename" command on the "Refactor" menu to change the interface name "IService1" in both code and config file together.    [ServiceContract]    public interface IService1    {        [OperationContract]        string GetData(int value);        [OperationContract]        CompositeType GetDataUsingDataContract(CompositeType composite);        // TODO: Add your service operations here    }    // Use a data contract as illustrated in the sample below to add composite types to service operations.    [DataContract]    public class CompositeType    {        bool boolValue = true;        string stringValue = "Hello ";        [DataMember]        public bool BoolValue        {            get { return boolValue; }            set { boolValue = value; }        }        [DataMember]        public string StringValue        {            get { return stringValue; }            set { stringValue = value; }        }    }}

using System;using System.Collections.Generic;using System.Linq;using System.Runtime.Serialization;using System.ServiceModel;using System.ServiceModel.Web;using System.Text;namespace SimpleServer{    // NOTE: You can use the "Rename" command on the "Refactor" menu to change the class name "Service1" in code, svc and config file together.    // NOTE: In order to launch WCF Test Client for testing this service, please select Service1.svc or Service1.svc.cs at the Solution Explorer and start debugging.    public class Service1 : IService1    {        public string GetData(int value)        {            return string.Format("You entered: {0}", value);        }        public CompositeType GetDataUsingDataContract(CompositeType composite)        {            if (composite == null)            {                throw new ArgumentNullException("composite");            }            if (composite.BoolValue)            {                composite.StringValue += "Suffix";            }            return composite;        }    }}

启动之后,下载WSDL文件 Service1.wsdl。

<?xml version="1.0" encoding="utf-8"?><wsdl:definitions name="Service1" targetNamespace="http://tempuri.org/" xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/" xmlns:wsap="http://schemas.xmlsoap.org/ws/2004/08/addressing/policy" xmlns:wsa10="http://www.w3.org/2005/08/addressing" xmlns:tns="http://tempuri.org/" xmlns:msc="http://schemas.microsoft.com/ws/2005/12/wsdl/contract" xmlns:soapenc="http://schemas.xmlsoap.org/soap/encoding/" xmlns:wsx="http://schemas.xmlsoap.org/ws/2004/09/mex" xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/" xmlns:wsam="http://www.w3.org/2007/05/addressing/metadata" xmlns:wsa="http://schemas.xmlsoap.org/ws/2004/08/addressing" xmlns:wsp="http://schemas.xmlsoap.org/ws/2004/09/policy" xmlns:wsaw="http://www.w3.org/2006/05/addressing/wsdl" xmlns:soap12="http://schemas.xmlsoap.org/wsdl/soap12/" xmlns:wsu="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd" xmlns:xsd="http://www.w3.org/2001/XMLSchema"><wsdl:types><xs:schema elementFormDefault="qualified" targetNamespace="http://tempuri.org/" xmlns:xs="http://www.w3.org/2001/XMLSchema"><xs:import namespace="http://schemas.datacontract.org/2004/07/SimpleServer"/><xs:element name="GetData"><xs:complexType><xs:sequence><xs:element minOccurs="0" name="value" type="xs:int"/></xs:sequence></xs:complexType></xs:element><xs:element name="GetDataResponse"><xs:complexType><xs:sequence><xs:element minOccurs="0" name="GetDataResult" nillable="true" type="xs:string"/></xs:sequence></xs:complexType></xs:element><xs:element name="GetDataUsingDataContract"><xs:complexType><xs:sequence><xs:element minOccurs="0" name="composite" nillable="true" type="q1:CompositeType" xmlns:q1="http://schemas.datacontract.org/2004/07/SimpleServer"/></xs:sequence></xs:complexType></xs:element><xs:element name="GetDataUsingDataContractResponse"><xs:complexType><xs:sequence><xs:element minOccurs="0" name="GetDataUsingDataContractResult" nillable="true" type="q2:CompositeType" xmlns:q2="http://schemas.datacontract.org/2004/07/SimpleServer"/></xs:sequence></xs:complexType></xs:element></xs:schema><xs:schema attributeFormDefault="qualified" elementFormDefault="qualified" targetNamespace="http://schemas.microsoft.com/2003/10/Serialization/" xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:tns="http://schemas.microsoft.com/2003/10/Serialization/"><xs:element name="anyType" nillable="true" type="xs:anyType"/><xs:element name="anyURI" nillable="true" type="xs:anyURI"/><xs:element name="base64Binary" nillable="true" type="xs:base64Binary"/><xs:element name="boolean" nillable="true" type="xs:boolean"/><xs:element name="byte" nillable="true" type="xs:byte"/><xs:element name="dateTime" nillable="true" type="xs:dateTime"/><xs:element name="decimal" nillable="true" type="xs:decimal"/><xs:element name="double" nillable="true" type="xs:double"/><xs:element name="float" nillable="true" type="xs:float"/><xs:element name="int" nillable="true" type="xs:int"/><xs:element name="long" nillable="true" type="xs:long"/><xs:element name="QName" nillable="true" type="xs:QName"/><xs:element name="short" nillable="true" type="xs:short"/><xs:element name="string" nillable="true" type="xs:string"/><xs:element name="unsignedByte" nillable="true" type="xs:unsignedByte"/><xs:element name="unsignedInt" nillable="true" type="xs:unsignedInt"/><xs:element name="unsignedLong" nillable="true" type="xs:unsignedLong"/><xs:element name="unsignedShort" nillable="true" type="xs:unsignedShort"/><xs:element name="char" nillable="true" type="tns:char"/><xs:simpleType name="char"><xs:restriction base="xs:int"/></xs:simpleType><xs:element name="duration" nillable="true" type="tns:duration"/><xs:simpleType name="duration"><xs:restriction base="xs:duration"><xs:pattern value="\-?P(\d*D)?(T(\d*H)?(\d*M)?(\d*(\.\d*)?S)?)?"/><xs:minInclusive value="-P10675199DT2H48M5.4775808S"/><xs:maxInclusive value="P10675199DT2H48M5.4775807S"/></xs:restriction></xs:simpleType><xs:element name="guid" nillable="true" type="tns:guid"/><xs:simpleType name="guid"><xs:restriction base="xs:string"><xs:pattern value="[\da-fA-F]{8}-[\da-fA-F]{4}-[\da-fA-F]{4}-[\da-fA-F]{4}-[\da-fA-F]{12}"/></xs:restriction></xs:simpleType><xs:attribute name="FactoryType" type="xs:QName"/><xs:attribute name="Id" type="xs:ID"/><xs:attribute name="Ref" type="xs:IDREF"/></xs:schema><xs:schema elementFormDefault="qualified" targetNamespace="http://schemas.datacontract.org/2004/07/SimpleServer" xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:tns="http://schemas.datacontract.org/2004/07/SimpleServer"><xs:complexType name="CompositeType"><xs:sequence><xs:element minOccurs="0" name="BoolValue" type="xs:boolean"/><xs:element minOccurs="0" name="StringValue" nillable="true" type="xs:string"/></xs:sequence></xs:complexType><xs:element name="CompositeType" nillable="true" type="tns:CompositeType"/></xs:schema></wsdl:types><wsdl:message name="IService1_GetData_InputMessage"><wsdl:part name="parameters" element="tns:GetData"/></wsdl:message><wsdl:message name="IService1_GetData_OutputMessage"><wsdl:part name="parameters" element="tns:GetDataResponse"/></wsdl:message><wsdl:message name="IService1_GetDataUsingDataContract_InputMessage"><wsdl:part name="parameters" element="tns:GetDataUsingDataContract"/></wsdl:message><wsdl:message name="IService1_GetDataUsingDataContract_OutputMessage"><wsdl:part name="parameters" element="tns:GetDataUsingDataContractResponse"/></wsdl:message><wsdl:portType name="IService1"><wsdl:operation name="GetData"><wsdl:input wsaw:Action="http://tempuri.org/IService1/GetData" message="tns:IService1_GetData_InputMessage"/><wsdl:output wsaw:Action="http://tempuri.org/IService1/GetDataResponse" message="tns:IService1_GetData_OutputMessage"/></wsdl:operation><wsdl:operation name="GetDataUsingDataContract"><wsdl:input wsaw:Action="http://tempuri.org/IService1/GetDataUsingDataContract" message="tns:IService1_GetDataUsingDataContract_InputMessage"/><wsdl:output wsaw:Action="http://tempuri.org/IService1/GetDataUsingDataContractResponse" message="tns:IService1_GetDataUsingDataContract_OutputMessage"/></wsdl:operation></wsdl:portType><wsdl:binding name="BasicHttpBinding_IService1" type="tns:IService1"><soap:binding transport="http://schemas.xmlsoap.org/soap/http"/><wsdl:operation name="GetData"><soap:operation soapAction="http://tempuri.org/IService1/GetData" style="document"/><wsdl:input><soap:body use="literal"/></wsdl:input><wsdl:output><soap:body use="literal"/></wsdl:output></wsdl:operation><wsdl:operation name="GetDataUsingDataContract"><soap:operation soapAction="http://tempuri.org/IService1/GetDataUsingDataContract" style="document"/><wsdl:input><soap:body use="literal"/></wsdl:input><wsdl:output><soap:body use="literal"/></wsdl:output></wsdl:operation></wsdl:binding><wsdl:service name="Service1"><wsdl:port name="BasicHttpBinding_IService1" binding="tns:BasicHttpBinding_IService1"><soap:address location="http://localhost:15418/Service1.svc"/></wsdl:port></wsdl:service></wsdl:definitions>

下面开始构建 gSoap 客户端。这次用的最新的gsoap_2.8.17.zip。

@echo off..\..\gsoap-2.8\gsoap\bin\win32\wsdl2h.exe -v -I..\..\gsoap-2.8\gsoap -o Service1.h Service1.wsdl 2>>soap.log@pause..\..\gsoap-2.8\gsoap\bin\win32\soapcpp2.exe -C  -j -I..\..\gsoap-2.8\gsoap;..\..\gsoap-2.8\gsoap\import;..\..\gsoap-2.8\gsoap\custom Service1.h 2>>soap.log@pause


客户端代码:

#include <iostream>#include <string>#include "soapBasicHttpBinding_USCOREIService1Proxy.h"#include "BasicHttpBinding_USCOREIService1.nsmap"int main(){    BasicHttpBinding_USCOREIService1Proxy proxy;    int value = 2;        _tempuri__GetData request;    request.value = &value;    _tempuri__GetDataResponse response;    int ret = proxy.GetData(&request, &response);    if (ret == SOAP_OK)    {        std::cout << *response.GetDataResult << std::endl;    }    else    {        proxy.soap_stream_fault(std::cerr);    }return 0;}

执行之后得到的错误信息为:

Error 415 fault: SOAP-ENV:Server[no subcode]"HTTP Error"Detail: HTTP/1.1 415 Cannot process the message because the content type 'application/soap+xml; charset=utf-8; action="http://tempuri.org/IService1/GetData"' was not the expected type 'text/xml; charset=utf-8'.

成功复现之前的错误。经过一些搜索,得到了答案。发送的请求中 content type 应该是 'text/xml; charset=utf-8',现在却是 'application/soap+xml; charset=utf-8; action="http://tempuri.org/IService1/GetData"‘。


修正的话,需要在生成C++代码是按照SOAP1.1标准,执行如下命令:

..\..\gsoap-2.8\gsoap\bin\win32\soapcpp2.exe -1 -C  -j -I..\..\gsoap-2.8\gsoap;..\..\gsoap-2.8\gsoap\import;..\..\gsoap-2.8\gsoap\custom Service1.h 2>>soap.log

重新编译之后,能够成功调用,输出

You entered: 2

按照搜索的结果,需要修改WSDL文件或nsmap文件,可能是gSoap版本更新后进行了修正。

后来查找有什么替代的方法,找到了 WWSAPI 的方法,缺点是不能跨平台,这里记录一下实现的方法。

首先生成WSDL对应的C++封装文件:

Wsutil.exe /wsdl:Service1.wsdl /string:WS_STRING

然后写客户端代码(根据MSDN WWSAPI HttpClientExample修改):

//------------------------------------------------------------// Copyright (C) Microsoft.  All rights reserved.//------------------------------------------------------------#ifndef UNICODE#define UNICODE#endif#include <windows.h>#include <stdio.h>#include <process.h>#include <string.h>#include "WebServices.h"#include "Service1.wsdl.h"// Print out rich error infovoid PrintError(HRESULT errorCode, WS_ERROR* error){    wprintf(L"Failure: errorCode=0x%lx\n", errorCode);    if (errorCode == E_INVALIDARG || errorCode == WS_E_INVALID_OPERATION)    {        // Correct use of the APIs should never generate these errors        wprintf(L"The error was due to an invalid use of an API.  This is likely due to a bug in the program.\n");        DebugBreak();    }    HRESULT hr = NOERROR;    if (error != NULL)    {        ULONG errorCount;        hr = WsGetErrorProperty(error, WS_ERROR_PROPERTY_STRING_COUNT, &errorCount, sizeof(errorCount));        if (FAILED(hr))        {            goto Exit;        }        for (ULONG i = 0; i < errorCount; i++)        {            WS_STRING string;            hr = WsGetErrorString(error, i, &string);            if (FAILED(hr))            {                goto Exit;            }            wprintf(L"%.*s\n", string.length, string.chars);        }    }Exit:    if (FAILED(hr))    {        wprintf(L"Could not get error string (errorCode=0x%lx)\n", hr);    }}// Main entry pointint __cdecl wmain(int argc, __in_ecount(argc) wchar_t **argv){    UNREFERENCED_PARAMETER(argc);    UNREFERENCED_PARAMETER(argv);        HRESULT hr = NOERROR;    WS_ERROR* error = NULL;    WS_CHANNEL* channel = NULL;    WS_HEAP* heap = NULL;    WS_ENDPOINT_ADDRESS address;    WS_STRING serviceUrl = WS_STRING_VALUE(L"http://localhost:15418/Service1.svc");        // Create an error object for storing rich error information    hr = WsCreateError(        NULL,         0,         &error);    if (FAILED(hr))    {        goto Exit;    }        // Create a heap to store deserialized data    hr = WsCreateHeap(        /*maxSize*/ 2048,         /*trimSize*/ 512,         NULL,         0,         &heap,         error);    if (FAILED(hr))    {        goto Exit;    }    //// Create a channel    hr = WsCreateChannel(        WS_CHANNEL_TYPE_REQUEST,         WS_HTTP_CHANNEL_BINDING,         NULL,         0,         NULL,         &channel,         error);    if (FAILED(hr))    {        goto Exit;    }     // Initialize address of service    address.url = serviceUrl;    address.headers = NULL;    address.extensions = NULL;    address.identity = NULL;        // Open channel to address    hr = WsOpenChannel(channel, &address, NULL, error);    if (FAILED(hr))    {        goto Exit;    }    WS_SERVICE_PROXY * serviceProxy;    hr = BasicHttpBinding_IService1_CreateServiceProxy(        NULL,        NULL,        0,        &serviceProxy,        error);    if (FAILED(hr))    {        goto Exit;    }    hr = WsResetMessage(requestMessage, error);    hr = WsOpenServiceProxy(serviceProxy, &address, NULL, error);    if (FAILED(hr))    {        goto Exit;    }    hr = WsResetMessage(requestMessage, error);    // Send some request-replies    for (int i = 0; i < 100; i++)    {             WS_STRING res;        hr = BasicHttpBinding_IService1_GetData(serviceProxy, 2, &res, heap, NULL, 0, NULL, error);                if (FAILED(hr))        {            goto Exit;        }                // Print out confirmation contents        wprintf(L"The Response is %.*s\n", res.length, res.chars);                // Reset the heap        hr = WsResetHeap(heap, error);        if (FAILED(hr))        {            goto Exit;        }    }    Exit:    if (FAILED(hr))    {        // Print out the error        PrintError(hr, error);    }    fflush(stdout);        if (channel != NULL)    {        // Close the channel        WsCloseChannel(channel, NULL, error);    }    if (channel != NULL)    {        WsFreeChannel(channel);    }        if (error != NULL)    {        WsFreeError(error);    }    if (heap != NULL)    {        WsFreeHeap(heap);    }    fflush(stdout);    return SUCCEEDED(hr) ? 0 : -1;}




参考链接:

http://www.blinnov.com/en/2008/01/22/wcf-service-unmanaged-client/

http://coab.wordpress.com/2009/10/15/calling-wcf-services-from-a-linux-c-client-using-gsoap/