gSoap编写代码访问WCF服务


最近给人写个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"' wa
s 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 info
void 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 point
int __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/



你可能感兴趣的:(编程,WCF,Visual,Studio,gsoap)