C++开发一个webservice,然后C#开发客户端,这样就需要C#的客户端访问gsoap的服务端。(大家都知道gsoap是C/C++开发webservice的最佳利器) 为什么不考虑直接用wcf开发webservice?那是考虑到以后有可能要移植到Linux平台。

gsoap实现C#(wpf)客户端调用C++服务器端

首先建立C++服务器端,这里需要使用到gsoap
第一步:自己在桌面建立一个文件夹,命名为add,打开add文件夹,新建txt文档,命名为add.txt,拷贝下面的代码到add.txt,点击保存,然后修改add.txt后缀名为add.h

//gsoap ns service name: add
//gsoap ns service namespace: http://localhost/add.wsdl
//gsoap ns service location: http://localhost
//gsoap ns service executable: add.cgi
//gsoap ns service encoding: encoded
//gsoap ns schema namespace: urn:add
int ns__add( int num1, int num2, int* sum );

第二步:从gsoap文件里面拷贝soapcpp2.exe到add文件夹里面,然后打开cmd命令行,输入:soapcpp2.exe add.h会生成许多文件,如图




第三步:新建Win32控制台应用程序,命名为:gsoapApplication_1_S;
添加wsock32.lib引用;

从add文件夹中拷贝如下图所示文件到该项目中,其中黑圈里面的两个项目来自gsoap拷贝:


第四步:修改gsoapApplication_1_S.cpp,其代码如下:
// gsoapApplication_1_S.cpp :
定义控制台应用程序的入口点。

//
#include "add.h"
#include "add.nsmap"
int main(int argc, char* argv[])
{
int m, s;
/* master and slave sockets */
struct soap add_soap;
soap_init(&add_soap);
//soap_set_namespaces(&add_soap, add_namespaces);
m = soap_bind(&add_soap, NULL, 4567, 100);
if (m < 0)
{
soap_print_fault(&add_soap, stderr);
exit(-1);
}
fprintf(stderr, "Socket connection successful: master socket = %d/n", m);
for ( ; ; )
{
s = soap_accept(&add_soap);
if (s < 0)
{
soap_print_fault(&add_soap, stderr);
exit(-1);
}
fprintf(stderr, "Socket connection successful: slave socket = %d/n", s);
soap_serve(&add_soap);//该句说明该server的服务
soap_end(&add_soap); }
return 0; }
//server端的实现函数与addmethod.h中声明的函数相同,但是多了一个当前的soap连接的参数
int ns__add(struct soap *add_soap, int num1, int num2, int *sum)
{
*sum = num1 + num2;
return SOAP_OK;
}

第五步:编译成功,打开cmd命令行,输入gsoapApplication_1_S.exe 4567打开主服务,如图


第六步:打开IE浏览器,输入:http://localhost:4567 回车,如图所示,服务器端已经配置成功

配置C#(wpf)客户端,使用到wsdl.exe,在本地搜索一下,没有的话需要在网上下载一个,我是在网上下载的,下载很简单

第一步:拷贝wsdl.exe到add文件夹,打开vs自带的黑屏命令行工具,输入:wsdl /out
add.wsdl回车,会生成.cs文件,文件里面包含多线程、代理等信息,如下图

第二步:新建WPF应用程序,命名为gsoapApplication_1_C_WPF,拷贝add.cs,add.wsdl两个文件到该工程中,(主要针对用别人的服务没法修改,亦可这样操作:右键点击C#窗口项目的“引用”选择“添加服务引用”,就会出现一个填写wsdl所在地址的对话框,可以选择是远端webservice服务地址下的wsdl,当然也可以是本地的。我选择的是本地地址,也就是C:\add.wsdl,点击“前往”。就会找到服务。这时下面的名字空间最好写的和当初gsoap建立时指定的命名空间一样。其实不一样也可以,但是在项目中使用的时候用的就是这个新的名字了。)修改add.cs文件代码的”http://localhost/add.cgi“为”http://localhost:4567/add.cgi“,如图所示:

第三步:MainWindow.xaml界面代码如下:

<Window x:Class="gsoapApplication_1_C_WPF.MainWindow"         xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"         xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="350" Width="525">
<Grid>
<GroupBox Header="Add" HorizontalAlignment="Left" Margin="52,43,0,0" VerticalAlignment="Top" Height="236" Width="412">
<Grid>
<Grid.RowDefinitions>
<RowDefinition/>
<RowDefinition/>
<RowDefinition/>
<RowDefinition/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition/>
<ColumnDefinition/>
</Grid.ColumnDefinitions>
<Grid Grid.Row="0" Grid.Column="0">
<Label Content="number 1:" HorizontalAlignment="Right" VerticalAlignment="Center"/> </Grid>
<Grid Grid.Row="0" Grid.Column="1">
<TextBox Name="num1" HorizontalAlignment="Left" VerticalAlignment="Center" Width="150" Height="30"/>
</Grid>
<Grid Grid.Row="1" Grid.Column="0">
<Label Content="number 2:" HorizontalAlignment="Right" VerticalAlignment="Center"/> </Grid>
<Grid Grid.Row="1" Grid.Column="1">
<TextBox Name="num2" HorizontalAlignment="Left" VerticalAlignment="Center" Width="150" Height="30"/>
</Grid>
<Grid Grid.Row="2" Grid.Column="0">
<Label Content="result----:" HorizontalAlignment="Right" VerticalAlignment="Center"/> </Grid>
<Grid Grid.Row="2" Grid.Column="1">
<TextBox Name="result" HorizontalAlignment="Left" VerticalAlignment="Center" Width="150" Height="30"/>
</Grid>
<Grid Grid.Row="3" Grid.Column="1">
<Button Name="submitAdd" Content="计算" Click="submitAdd_Click" Width="80" Height="30"/> </Grid>
</Grid>
</GroupBox>
</Grid>
</Window>

第四步:MainWindow.xaml.cs代码如下:

using System;
using System.Collections.Generic;
using System.Linq; using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
namespace gsoapApplication_1_C_WPF
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
}
private void submitAdd_Click(object sender, RoutedEventArgs e)
{
add myadd = new add();
int num1 = Convert.ToInt32(this.num1.Text);
int num2 = Convert.ToInt32(this.num2.Text);
int? sum = 0;
bool sumSpecified = false;
myadd.Calladd(num1, num2,out sum, out sumSpecified);
this.result.Text = sum.ToString();
}
}
}

第五步:编译,生成,结果如图所示(不要忘了打开主服务哦~):

发现新的问题

对于接口.h文件中用自定义的结构体,会出现无法辨识的问题。
例如:

//gsoap ns service name: add
//gsoap ns service namespace: http://localhost/add.wsdl
//gsoap ns service location: http://localhost
//gsoap ns service executable: add.cgi
//gsoap ns service encoding: encoded
//gsoap ns schema namespace: urn:add
int ns__add( int num1, int num2, int* sum );
typedef struct ns__point
{
int x;
int y;
};
int ns__addPt(struct ns__point num1, struct ns__point num2, struct ns__point* result );

1、C#提供的WebService的URL一般形如:http://localhost/WebService.asmx,但是,C++能提供的只能是:http://localhost/。C++做客户端的时候调用没有问题,但是当C#做客户端的时候,引用C++提供的RUL时,会提示没用执行方法(HTTP GET method not implemented)。做C#开发的大部分会认为C++方提供的不是WebService,或者说提供的WebService根本就不全,都不带.asmx文件。做C++开发的会认为他传输的数据符合soap协议,靠http传输数据,他就是WebService。
2、当我们解决了第一步后,紧接着会发现另外一个问题。当我们需要传输自定义数据类型时(在C++中称结构体,在C#中称实体),从C++返回的信息中,C#无法构建出实体类。

3、当传输的信息中带有中文字符时,乱码满天飞。

为了解决这些问题,我们先简单了解一下WebService。
Web Service互操作协议栈:
、服务发现 (UDDI)
、服务描述(WSDL)
、服务调用(SOAP)
、消息编码 (XML)

、传输网络层(HTTP, TCP/IP)

其中WSDL描述WebService都有什么方法、方法有什么参数,什么返回值等。SOAP(简单对象访问协议(Simple Object Access
Protocol)是一种轻量的、简单的、基于XML的协议。传输的数据就需要遵循这个协议。我比较简单得认为传输的数据需要遵循这种格式。

借用微软的这个图描述下WebService的调用过程:

C++、C写的WebService相互调用martin新浪博客 - mylovejsj - 宋孝先的博客

开始解决问题。作为.NET开发人员,我们根本就接触不到底层的东西,全被封装了。

C++做的确实是WebService,只是他们需要给提供一个描述文档,即.WSDL文件。使用.NET提供的wsdl.exe工具,使用命令:wsdl /o:
c:\webservice.cs
c:\webservice.wsdl。通过webservice.wsdl文档,生成代理类,将代理类写入webservice.cs文件中。我们拷贝这个cs文件到项目中,将URL指向http://localhost/,就能像以往那样使用WebService了。

当出现无法传递复杂类型数据时,是因为使用gsoap生成的wsdl文件与.Net中生成的wsdl文件不一样。具体代码如下:

<!-- operation response element -->
<element >
<complexType>
<sequence>
<element type="xsd:int" minOccurs="1" maxOccurs="1"/>
<element type="xsd:int" minOccurs="1" maxOccurs="1"/>
</sequence>
</complexType>
</element>

以上为gsoap生成的。返回实体result,实体有两个属性:a,b。

<s:element >
<s:complexType>
<s:sequence>
<s:element minOccurs="0" maxOccurs="1" type="tns:result" />
</s:sequence>
</s:complexType>
</s:element>
<s:complexType >
<s:sequence>
<s:element minOccurs="1" maxOccurs="1" type="s:int" />
<s:element minOccurs="1" maxOccurs="1" type="s:int" />
</s:sequence>
</s:complexType>

以上是.NET生成的。
在下面的文件中,多出

<s:element >
<s:complexType>
<s:sequence>
<s:element minOccurs="0" maxOccurs="1" type="tns:result" />
</s:sequence>
</s:complexType>
</s:element>

这个便是.NET中用来构造实体的。当我们出现情况4.2时,gsoap中尽量使用.NET生成的wsdl文档,生成.h文件,以避免C++中的结构无法在C#中转换成实体。

第三个问题,我们是通过将中文转换成16进制后传输过来,然后再转换成中文。下面提供C#转换的代码:

/// <summary>
/// 从16进制转换成汉字
/// </summary>
/// <param ></param>
/// <returns></returns>
public static string GetChsFromHex(string hex)
{
if (hex == null)
throw new ArgumentNullException("hex");
if (hex.Length % 2 != 0)
{
hex += "20";//空格
//throw new ArgumentException("hex is not a valid number!", "hex");
}
// 需要将 hex 转换成 byte 数组。
byte[] bytes = new byte[hex.Length / 2]; for (int i = 0; i < bytes.Length; i++)
{
try
{
// 每两个字符是一个 byte。
bytes[i] = byte.Parse(hex.Substring(i * 2, 2),
System.Globalization.NumberStyles.HexNumber);
}
catch
{
// Rethrow an exception with custom message.
throw new ArgumentException("hex is not a valid hex number!", "hex");
}
} // 获得 GB2312,Chinese Simplified。
System.Text.Encoding chs = System.Text.Encoding.GetEncoding("gb2312"); return chs.GetString(bytes);
} /// <summary>
/// 从汉字转换到16进制
/// </summary>
/// <param ></param>
/// <returns></returns>
public static string GetHexFromChs(string s)
{
if ((s.Length % 2) != 0)
{
s += " ";//空格
//throw new ArgumentException("s is not valid chinese string!");
} System.Text.Encoding chs = System.Text.Encoding.GetEncoding("gb2312"); byte[] bytes = chs.GetBytes(s); string str = ""; for (int i = 0; i < bytes.Length; i++)
{
str += string.Format("{0:X}", bytes[i]);
} return str;
}

注:以上来转换代码源于网络,C++中转换的代码也可以在网上找到。

例子解决代码

//gsoap ns service name: calc
//gsoap ns service style: rpc
//gsoap ns service encoding: encoded
//gsoap ns service namespace: http://127.0.0.1:8089/calc.wsdl
//gsoap ns service location: http://127.0.0.1:8089/cal.cgi
//gsoap ns schema namespace: urn:calc
typedef struct ns__point
{
int x;
int y;
}; struct ns__pointResponse
{
int ret;
struct ns__point pt;
}; int ns__add(int num1, int num2, int* result );
int ns__addPt(struct ns__point num1, struct ns__point num2, struct ns__pointResponse* result );


图中会写下calc.cs,就说明成功了。
至于为什么这么写就解决问题了,那就要扯到如何生成接口头文件了。

由wsdl文件生成头文件

开发客户端程序
(1)客户端服务端通信必不可少的一个东西:接口定义。

gSoap使用中,需要此接口文件(也就是以后常提到的头文件)此文件可以两种方式得到:
1: 由现有的wsdl文件转换得到(本文使用这种方式)

2: 自己编写
(2)两个工具的用途
wsdl2h.exe——–名字就能看出来,完成
wsdl文件(.wsdl)到(2)头文件(.h)的转换,即由写好的wsdl格式的接口定义文件转换为C/C++格式的头文件

soapcpp2.exe——根据接口头文件生成应用程序客户端和服务端的框架文件(soapClient.cpp、soapServer.cpp等).

结合例子说明过程
示例:引用向导中的例子–计算器
由wsdl文件生成接口头文件
wsdl文件URL地址:http://www.cs.fsu.edu/~engelen/calc.wsdl
操作流程
a)
程序->运行 输入cmd 启动命令行;
b) 打开wsdl2h.exe所在文件路径(不打开的话下面输入时要记着输入全路径,不然找不着了);

c) 输入:wsdl2h.exe -o cal.h http://www.cs.fsu.edu/~engelen/calc.wsdl,
其中

“wsdl2h.exe”,因为第二步中打了wsdl2h文件所在路径,所以此处可以直接用了,若没打开要输入所在完整路径

“cal.h”为生成的头文件名,此文件的保存位置在wsdl2h.exe同一文件目录下(要修改的话要请参照命令行语法规则,此处不多说了,有点脱离主题)

       “-o”是生成选项,表示输出,也可以增加其它选项(向导中出现 “-s -o”、”-c -o”等),具体选项说明请参照向导中介绍。

http://www.cs.fsu.edu/~engelen/calc.wsdl“是wsdl文件的位置,此处是在网络地址(这个地址是可以用的),如果wsdl文件在本地的也可以,路径设置好就OK了

注意:以上四项中每项间都有空格分开 不要手误啊!
d) 回车
运行吧,看提示信息,没有提示故障的话,查找cal.h吧(cal.h的保存路径要记好啊,自己设置的);
e) 得到cal.h后,打开可以看到接口定义;

f) 在命令行中 输入:soapcpp2.exe cal.h
其中

“soapcpp2.exe”,此处把soapcpp2.exe和wsdl2h.exe放在同一目录下了,且前面已打开此路径;

“cal.h”即由wsdl生成的头文件,此文件保存位置与soapcpp2.exe相同,故此处可这样输入,若不同还须设置文件路径;

同样两项之间也有空格!
可以增加控制选项如”soapcpp2.exe -C cal.h”则只生成客户端所需文件, “soapcpp2.exe
-S calc.h”则只生成服务端所需文件
g) 回车 运行。
如果提示找不到文件
stlvector.h,因为cal.h文件中引用了文件stlvector.h;解决方法:开发包\gsoap\import目下有此文件,复制一份到cal.h所在目录。

注意:此过程中要检查生成的编译信息,确保没有警告信息出现(警告信息可能是gSoap不支持的内容,如果不去除,程序运行故障很难找到根本原因),另不能只看编译结果成功就认为是正确的,存在警告信息也显示为编译成功!!

h)
编译成功,生成的框架文件保存位置与cal.h在同一目录下,包括:calc.nsmap、soapC.cpp、soapcalcObject.h、soapcalcProxy.h、soapClient.cpp、soapClient.lib

、soapH.h、soapServer.cpp、soapServer.Lib、soapStub.h和接口的请求应答xml文件。
    其中

      calc.nsmap 为命名空间定义文件;
      soapClient.cpp 客户端文件

soapServer.cpp 服务端文件
soapcalcProxy.h 代理用到
soapStub.h
此文件中为程序中要用到的接口的声明(将cal.h中的接口经过处理后,在程序中供调用的接口)
xml文件 为接口的xml格式,可以据此帮助调试等。。。

其它文件作用还没完全搞明白,向导中有说明,此处不说了以免误导^_^
i) 编译过程完成。下面开始程序(客户端)

VC6中新建空的win32控制台工程,新建源文件calcClient.cpp;

复制编译生成的文件(包括cal.h和stlvector)到程序目录中;

在工程中添加文件calc.nsmap、soapC.cpp、soapClient.cpp、soapH.h、soapStub.h(使用代理添加soapcalcProxy.h);

编译,提示无法找到文件stdsoap2.h、stdsoap2.cpp文件,这两个文件为gSoap的源码文件,在\gsoap文件夹中找到这两个文件,复制到程序目录下并添加到工程中;

在calcClient.cpp文件中 添加语句#include “soapH.h”, #include “calc.nsmap”

///////////////////////////calcClient.cpp源码//

#include <stdio.h>
#include "soapH.h"
#include "calc.nsmap"
const char server[] = "http://websrv.cs.fsu.edu/~engelen/calcserver.cgi";
int main(int argc, char **argv)
{
struct soap soap;
double a, b, result;
if (argc < 4)
{
fprintf(stderr, "Usage: [add|sub|mul|div|pow] num num\n");
exit(0);
}
soap_init(&soap);
a = strtod(argv[2], NULL);
b = strtod(argv[3], NULL);
switch (*argv[1])
{ case 'a':
soap_call_ns2__add(&soap, server, "", a, b, result);
break;
case 's':
soap_call_ns2__sub(&soap, server, "", a, b, result);
break;
case 'm':
soap_call_ns2__mul(&soap, server, "", a, b, result);
break;
case 'd':
soap_call_ns2__div(&soap, server, "", a, b, result);
break;
case 'p':
soap_call_ns2__pow(&soap, server, "", a, b, result);
break;
default:
fprintf(stderr, "Unknown command\n");
exit(0);
}
if (soap.error)
{
soap_print_fault(&soap, stderr);
exit(1);
}
else
printf("result = %g\n", result);
soap_destroy(&soap);
soap_end(&soap);
soap_done(&soap);
return 0;
}

其中soap_call_ns2__add、soap_call_ns2__sub、soap_call_ns2__mul、soap_call_ns2__div、soap_call_ns2__pow为接口函数,在soapStub.h中可以找到函数声明。

自定义接口头文件

接口头文件的格式在向导中没有看到明确的说明性的内容,但通过看开发包中示例程序中头文件定义和通过wsdl生成的头文件的内容,可以发现,头文件中都会出现以下几行信息
//gsoap ns service name: calc Simple calculator service
//gsoap ns
service style: rpc
//gsoap ns service encoding: encoded
//gsoap ns
service namespace: http://websrv.cs.fsu.edu/~engelen/calc.wsdl

//gsoap ns service location: http://websrv.cs.fsu.edu/~engelen/calcserver.cgi
//gsoap
ns service method-style: add rpc
//gsoap ns service method-encoding: add http://schemas.xmlsoap.org/soap/encoding/
//gsoap ns
service method-action: add “”

这些都是头文件中的说明性信息,编译时会用到这里的信息,这些信息有命名空间的和服务名称的名字,有些信息为默认参数信息,如service location
如果在客户端调用接口方法时,没有输入服务端地址,方法执行时将自动调用此处的地址做为服务器地址。后三行是对add方法的说明,在写头文件时可以修改一下这些说明信息如下:

//gsoap ns service name: calc Simple calculator service
//gsoap ns
service style: rpc
//gsoap ns service namespace: urn:calc
//gsoap ns
service location: http://websrv.cs.fsu.edu/~engelen/calcserver.cgi

这些信息是很重要的,如果是已定义好的接口,一定要注意命名空间名字统一。
下面开始主要工作:接口定义

gSoap对“”和“_”(下划线、双下划线)有特殊用法,接口定义时函数名前要加上命名空间名和双下划线
举例:加法运算

int add(int num1, int num2, int &num3);
在接口头文件定义时要写成如下格式
int
ns__add(int num1, int nmu2, int &num3);
其中
“ns”是命名空间名称,此名称可以自定义成其它名字
但其后”urn:calc”不可随意更改,这个是要与服务端统一的
“__”是编译时识别符号,如果没有的话xml文件是编译不出来的

“_”(单下划线)定义名称中要慎用,自己定义的变量或函数名称中使用下划线的地方要在下划线后加上”USCORE”;
例 定义
ns__add_sum(int num_1, int num2, int &num_3);
要写成如下形式

ns__add_USCOREsum(int num_USCORE1, int num2, int &num_USCORE3);

“add”是接口函数名 这个是客户端与服务器统一的接口名字
函数内参数变量类型和变量名 为客户端与服务器统一的接口

函数中只有最后一个参数是输出参数,前面的都是输入参数
若有多个输出信息,可定义结构体。
若没有输入参数 可将输入参数类型定义为void
(将忽略void 类型的参数)
函数返回类型必须为 int,可以通过返回值判断接口函数执行情况

至此接口定义要注意的地方完毕,开始写接口定义文件。

本例中使用自定义的头文件,编写客户端程序,但服务器仍与上篇中服务器相同,以便说明两种方式的结果,接口可以从上篇中由wsdl生成的头文件中获得。

源码如下:

///////////////////mycalc.h///////////////////////////////
//gsoap ns service name: mycalc
//gsoap ns service style: rpc
//gsoap ns service namespace: urn:calc
//gsoap ns service location: http://websrv.cs.fsu.edu/~engelen/calcserver.cgi
int ns__add(double a, double b, double &result);
int ns__sub(double a, double b, double &result);
int ns__mul(double a, double b, double &result);
int ns__div(double a, double b, double &result);
int ns__pow(double a, double b, double &result);
///////////////////mycalc.h///////////////////////////////

上面文件中一定要注意 //gsoap ns service namespace: urn:calc 由于服务器中接口命名空间为calc 故此处必须是calc!
开始编译此头文件
运行 soapcpp2.exe mycalc.h
编译成功,写客户端程序

新建工程mycalcClient, 添加生成的文件,新建源文件mycalcClient.cpp
源码如下

//////////////////////mycalcClient.cpp/////////////////
#include <stdio.h>
#include "soapH.h"
#include "calc.nsmap"
const char server[] = "http://websrv.cs.fsu.edu/~engelen/calcserver.cgi";
int main(int argc, char **argv)
{
struct soap soap;
double a, b, result;
if (argc < 4)
{ fprintf(stderr, "Usage: [add|sub|mul|div|pow] num num\n");
exit(0);
}
soap_init(&soap);
a = strtod(argv[2], NULL);
b = strtod(argv[3], NULL);
switch (*argv[1])
{ case 'a':
soap_call_ns__add(&soap, server, "", a, b, result);
break;
case 's':
soap_call_ns__sub(&soap, server, "", a, b, result);
break;
case 'm':
soap_call_ns__mul(&soap, server, "", a, b, result);
break;
case 'd':
soap_call_ns__div(&soap, server, "", a, b, result);
break;
case 'p':
soap_call_ns__pow(&soap, server, "", a, b, result);
break;
default:
fprintf(stderr, "Unknown command\n");
exit(0);
}
if (soap.error)
{ soap_print_fault(&soap, stderr);
exit(1);
}
else
printf("result = %g\n", result);
soap_destroy(&soap);
soap_end(&soap);
soap_done(&soap);
return 0;
}
//////////////////////mycalcClient.cpp/////////////////

测试一下吧^_^
对比两种生成接口头文件的方法,选择那一种还要视情况而定

如果是已有写好的wsdl文档,当然是选择用wsdl2h工具来生成头文件了,毕竟这个方便的多,但在编译时,一定要检查警告信息,有些情况下生成的接口头件存在编译异常,例如复杂数据结构时就有可能出现结构体命名不符合gSoap命名规则(实际应用中遇至过,此时要手动修改这些命名)

简单说一下soap结构体初始化及清理
soap_init(&soap);//初始化
…………

soap_destroy(&soap);//结束清理操作
soap_end(&soap);

soap_done(&soap);
向导中这方面讲的比较细^_^

两篇都只是写了客户端的程序,因为是一直在与现成的服务器端通信,在此就没列出自己的服务器程序;

接口定义

,可参考《GSoap接口定义》。这里我将给出C#引用这个webserver所对应的接口形式。

gsoap是根据我们定义好的.h文件,然后用工具产生了我们所需的.c文件。所以我们必须根据gsoap的要求编写.h。
1.
单个参数的传出:

int ns__add( int a, int b, int *c );

需要说明的是,这里的ns__是必须的,必须以开始注释中的ns加两个下划线开始。返回值必须是int。

但是这里的int并不是接口的返回值,而是gsoap内部的返回值。真正的返回值是int *c。

 C#中对应的接口:  int add( int a, int b );  返回值就是上述的int *c参数。

2. 多个参数传出,在接口中必须使用结构体

 typedef char * xsd__string;
typedef long xsd__int;
struct ns__personResponse{
xsd__int age;
xsd__string name;
xsd__string address;
};
int ns__person( xsd__string buf_in, struct ns__personResponse * buf_out );
 在C#中,并不是我们所声明的这样。而是:int person( string buf_in, out string name, out string address );
即,结构体中的第一个域会变成返回值,其他的变成一个个的输出参数。

3. 返回结构体。如果要返回结构图,那么必须在结构体中再套一层结构体:

 typedef char * xsd__string;
typedef long xsd__int;
struct ns__person{
xsd__int age;
xsd__string name;
xsd__string address;
};
struct ns__personResponse{
xsd__int ret;
struct ns__person person;
};
int ns__person( xsd__string buf_in, struct ns__personResponse * buf_out );
 那么在C#中,看到的接口是这样的:int person( string buf_in, person对应的结构类 );

4. 接口中的下划线,如果接口中的交易名有下划线,必须这么声明:
int
ns__echo_USCOREreverse( char * buf_in, char ** buf_out );

 那么,C#中实际上的接口名就是:string echo_reverse( string buf_in );

gsoap中返回字符串
1. 下面是一个逆转字符串的函数。

int ns__echo_USCOREreverse( char * buf_in, char ** buf_out );
int ns__echo_USCOREreverse( struct soap *add_soap, char *buf_in, char **buf_out )
{
int i, j, len;
printf( "ns__interface: in=[%s]\n", buf_in ); len = strlen(buf_in);
*buf_out = (char*)soap_malloc( add_soap, len+1 );
for( i=len-1, j=0; i>=0; i--, j++ ){
(*buf_out)[j] = buf_in[i];
}
(*buf_out)[j] = 0; return 0;
}
 其中调用soap_malloc申请空间,并且将他赋给返回参数buf_out。这个空间会在调用soap_end时被释放。

gsoap传输中文。我使用utf-8编码格式来支持汉字的传输。
1. 设置gsoap为utf-8传输数据
soap_set_mode(
&SmsWBS_soap, SOAP_C_UTFSTRING ); //设置编码

SmsWBS_soap.mode|=SOAP_C_UTFSTRING;

  1. 使用下面得函数转换我们的传输内容,即将我们的数据转成UTF-8编码:
int conv_charset( const char *dest, const char *src, char *input, size_t ilen, char *output, size_t olen )
{
int convlen = olen;
iconv_t conv = iconv_open( dest, src );
if( conv == (iconv_t) -1 )
return -1; memset( output, 0, olen );
if( iconv( conv, &input, &ilen, &output, &olen ) ){
iconv_close(conv);
return -1;
} iconv_close(conv);
return convlen-olen;
}
 例子: conv_charset( "UTF-8", "GBK", "林学任.linxr", strlen("林学任.linxr"),  buf_out->name, 100 );

webserver发布
1.
在C#中,可以直接引用一个webserver,但是我们写得webserver如何能用被其引用呢。其实只要实现gsoap的fget回调函数即可:

SmsWBS_soap.fget = http_get;
2.
http_get函数实现

int http_get(struct soap * soap)
{
FILE *fd = NULL; char *s = strchr( soap->path, '?' );
if( !s || strcmp( s, "?wsdl" ) ){
return SOAP_GET_METHOD;
} fd = fopen( "SmsWBS.wsdl", "rb" );
if (!fd){
return 404;
} soap->http_content = "text/xml";
soap_response(soap, SOAP_FILE);
for (;;){
size_t r = fread(soap->tmpbuf, 1, sizeof(soap->tmpbuf), fd);
if( !r ){
break;
}
if( soap_send_raw( soap, soap->tmpbuf, r) ){
break;
}
} fclose(fd);
soap_end_send(soap); return SOAP_OK;
}

Reference:[gsoap使用总结](http://www.cnblogs.com/linxr/archive/2011/10/17/2215285.html)

转自:https://blog.csdn.net/caowei880123/article/details/49129211

C#访问gsoap的服务的更多相关文章

  1. C#访问gsoap的服务--可用

    问题来源: C++开发一个webservice,然后C#开发客户端,这样就需要C#的客户端访问gsoap的服务端.(大家都知道gsoap是C/C++开发webservice的最佳利器) 为什么不考虑直 ...

  2. 重写TiledServiceLayer实现Arcgis访问Mapabc地图服务 (转载)

    package com.baixin.main;/** *  * @ClassName:   MapAbcToArcGISTLayer * @Description:    ArcGIS访问MapAb ...

  3. SpringCloud系列八:Zuul 路由访问(Zuul 的基本使用、Zuul 路由功能、zuul 过滤访问、Zuul 服务降级)

    1.概念:Zuul 路由访问 2.具体内容 在现在为止所有的微服务都是通过 Eureka 找到的,但是在很多的开发之中为了规范微服务的使用,提供有一个路由的处理控制组件:Zuul,也就是说 Zuul ...

  4. Istio如何使用相同的端口访问网格外服务

    1.1.背景 写这篇文章的目的是为了说明以下问题:如何使用TCP协议相同的端口访问网格外多个服务? 这是最近直播的时候有一个同学提出的,当时我没有完全明白,“访问多集群” 的意思.后来仔细思考了一下, ...

  5. Linux通过端口转发来访问内网服务(端口转发访问阿里云Redis数据库等服务)

    # 安装rinetd wget http://www.boutell.com/rinetd/http/rinetd.tar.gz&&tar -xvf rinetd.tar.gz& ...

  6. VC++ 使用MSSOAP访问WebService天气服务(客户端开发)

    绪论 本文介绍使用VC++编程实现访问天气Web服务的简单实例(例子来源于网络). Web天气服务 http://www.webxml.com.cn/WebServices/WeatherWebSer ...

  7. 访问公网WebService服务

    接下来,我们演示如何访问公网webservice服务. 我们以访问 http://www.webxml.com.cn/zh_cn/index.aspx 为例,主要演示手机号码归属地查询服务(使用说明书 ...

  8. 不能访问windows installer 服务,可能你在安全模式下运行 windows ,或者windows installer

    windows installer服务解决方案 很多朋友在安装MSI格式的文件包时,经常会遇到windows installer出错的情况,有如下几种现象: 1.所有使用windows install ...

  9. 不能访问windows installer服务

    xp系统安装msi类型的安装程序出现以下错误: 不能访问windows installer服务. 解决办法 1:运行cmd -> regsvr32 msi.dll 运行services.msc- ...

随机推荐

  1. MySql中的事务、JDBC事务、事务隔离级别

    一.MySql事务 之前在Oracle中已经学习过事务了,这个东西就是这个东西,但是在MySql中用法还是有一点不同,正好再次回顾一下. 先看看MySql中的事务,默认情况下,每执行一条SQL语句,都 ...

  2. FZU2279 : Cantonese

    首先把相同的事件点合并,那么每个点有时间$t_i$,位置$x_i$,价值$v_i$. 考虑DP,设$f_i$表示按时间从小到大考虑每个事件,目前位于事件$i$的时间与位置时,最多能让多少个事件发生.在 ...

  3. Django——权限

    在models中为user添加权限,permissions第二个参数可指定权限的别名 创建的各种用户user保存在auth_user表中 创建的各种用户user的对应权限permission_id保存 ...

  4. Java weak reference

    一个对象被回收,必须满足两个条件: 没有任何引用指向它和GC在运行.把所有指向某个对象的引用置空来保证这个对象在下次GC运行时被回收. Object c = new Car(); c = null; ...

  5. 使用itchat实现一个微信机器人聊天回复功能

    近看到好多群里都有一个@机器人的功能,挺有趣的,想自己也玩下,就通过百度一点点实现,在这总结一下整个从无到有的过程. 首先,要知道itchat,它是Python写的,所以想要实现这个机器人的功能,需要 ...

  6. 通过pip安装python web

    提示 No module named 'utils' 我就pip install utils 提示 No module named 'db' 然后我就 pip install db 报错 No mod ...

  7. sench touch 页面跳转

    下面是我做的一个简单的登录页面登录成功后跳转页面 首页要在app.js 里面添加 1.视图层   登录页面 Ext.define('MyApp.view.Login', { extend: 'Ext. ...

  8. Annotation 的第一个工程

    一.什么是 Annotation? java.lang.annotation,接口 Annotation.对于Annotation,是Java5的新特性,JDK5引入了Metadata(元数据)很容易 ...

  9. kvm部署

    第一:安装前准备 vmware workstation的虚拟机做kvm实验,需要开启嵌套虚拟化 1.首先在物理机BIOS设置里开启虚拟化功能 2.其次需要在vm里面开启一下两个功能,(关闭虚拟机勾选即 ...

  10. 使用linux kernel代码编译perf工具

    环境:Qemu + ARMv8 perf是一款综合性分析工具,大到系统全局性性能,再小到进程线程级别,甚至到函数及汇编级别. 在内核源码目录下执行编译脚本: #!/bin/bash cross_com ...