• 学习笔记Web服务、Remoting、WCF (上) Web服务


    自从程序设计诞生起, 如何最大限度的重用代码, 减小编码的重复劳动就是程序员永恒不变的课题. 我们从学习面向对象开始, 先后引入了很多种共享代码的手段, 随着学习的不断深入就越发觉得程序设计的发展过程其实就是不断的提高代码重用能力的过程. 如: 最先学到的继承就是一种共享代码的手段, 子类自动继承父类的所有成员也可以说是子类共享父类的所有代码; 再如: 虚方法, 当子类需要时便可以重写该方法, 否则共享父类的这个方法;

    之后还有抽象类、接口、多肽等, 抽象类和接口定义了某种子类必须遵循的规定, 而我们可以用这些规定代表那些遵循规定的子类, 并且不同的子类可以表现出不同的行为, 这就是多肽, 那么共享代码发生在哪里呢? 发生在程序的处理逻辑, 我们可以用抽象类、接口等参与代码的处理逻辑而忽略掉子类之间的异同, 实际上这里共享了程序处理逻辑的代码. 因此, 我觉得面向对象的发展过程就是代码共享不断升级的过程.

    现在, Web服务、Remoting、WCF其实也是代码共享的手段, 只不过这些手段比较高级, 是通过网络来共享代码的而已.

    可以参加下图:

    1. Web服务:

    Web服务的基本思想是: 通过本地代理类远程调用服务器上的方法并返回结果. 底层基于SOAP协议(HTTP+XML), 本质上就是用HTTP POST请求向服务器发出一个SOAP格式的XML文件.

    SOAP(Simple Object Access Protocol, 简单对象访问协议)是一个轻量级协议, 是基于XML的, 它主要关心数据的格式(或者说数据的表示)而不是数据的传输, 真正的数据传输工作是使用其他通讯协议(通常使用HTTP协议)来完成的. 由于SOAP采用HTTP来传输数据, 而HTTP协议传输数据的方式无非就是GET和POST两种方式(GET方式数据放在URL中且最大为1K, 而POST方式数据放在HTTP协议包的包体中), 显然SOAP更易采用POST方式, 因此, 调用Web服务上的方法其实就是使用HTTP POST向服务器发出一个SOAP格式的XML文件(SOAP = HTTP + XML).

    Web服务的通信过程及SOAP协议的基本结构见下图:

    a. 创建Web服务:

    创建Web服务可以有两种方式: 通过Visual Studio创建和手动创建. 通过Visual Studio创建Web服务非常简单, 只要VS中选择 文件->新建->网站, 选中Web服务确定即可.

    手动创建Web服务可以更容易帮我们理解Web服务:

    新建文件夹CodeShare, 在该文件夹下用任何文本编辑器(如:记事本)创建扩展名为asmx的web服务文件, 并写入如下指令, 其中CodeBehind用于指出用来共享代码的文件的位置,Class用于指出共享代码的类的名称(带命名空间):

    <%@ WebService Language=”C#” CodeBehind=”~/App_Code/Filename.cs” Class=”WS.ShareClass”%>

    将要共享代码的类置于CodeShare文件夹下的App_Code文件夹中, 即Filename.cs文件中的WS命名空间下的ShareClass类需要继承自System.Web.Services.WebService, 之后在ShareClass中写方法即可, 记得在方法前加[WebMethod]. 将asmx所在的文件夹发布到IIS中, 就可以对外提供服了. 注意: App_Code文件夹是必须的, 猜想可能是在IIS中将针对asmx的请求送给aspnet_iisapi.dll后会交给ASP.NET处理, 而ASP.NET会到App_Code文件夹中寻找类库文件.

    Web服务示例 --- 服务器代码

    //CodeShare/webservice.asmx

    <%@ WebService Language="C#" CodeBehind="~/App_Code/Service.cs" Class="WS.Calculate"%>

    //CodeShare/App_Code/Service.cs

    代码
    using System;
    using System.Collections.Generic;

    namespace WS
    {
    public class Calculate : System.Web.Services.WebService
    {
    #region 利用堆栈实现计算器
    [System.Web.Services.WebMethod]
    public double Compute(string expression)
    {
    //处理最开始的-或者+号
    if (expression.StartsWith("+") || expression.StartsWith("-"))
    {
    expression
    = "0" + expression;
    }

    //1. 将+、-、*、/分离成出来
    expression = expression.Replace("+", ",+,");
    expression
    = expression.Replace("-", ",-,");
    expression
    = expression.Replace("*", ",*,");
    expression
    = expression.Replace("/", ",/,");

    string[] infos = expression.Split(',');
    Stack
    <string> stack = new Stack<string>(); //使用list模拟堆栈

    //2. 利用堆栈计算乘除的结果
    double prenum;
    double nextnum;
    for (int i = 0; i < infos.Length; i++)
    {
    if (infos[i] == "*" || infos[i] == "/")
    {
    prenum
    = Convert.ToDouble(stack.Pop());
    nextnum
    = Convert.ToDouble(infos[i + 1]);
    if (infos[i] == "*")
    {
    stack.Push((prenum
    * nextnum).ToString());
    }
    else
    {
    stack.Push((prenum
    / nextnum).ToString());
    }
    i
    ++; //别忘了i要++
    }
    else
    {
    stack.Push(infos[i]);
    }
    }

    //3. 利用堆栈计算加减的结果

    //infos = stack.ToArray(); //将stack转存到数组中, 这里转存的结果是逆序的
    infos = new string[stack.Count];
    for (int i = infos.Length-1; i >= 0; i--) //将stack正序转存到数组中
    {
    infos[i]
    = stack.Pop();
    }

    prenum
    = 0; //重置prenum和nextnum
    nextnum = 0;
    for (int i = 0; i < infos.Length; i++)
    {
    if (infos[i] == "+" || infos[i] == "-")
    {
    prenum
    = Convert.ToDouble(stack.Pop());
    nextnum
    = Convert.ToDouble(infos[i + 1]);
    if (infos[i] == "+")
    {
    stack.Push((prenum
    + nextnum).ToString());
    }
    else
    {
    stack.Push((prenum
    - nextnum).ToString());
    }
    i
    ++; //别忘了i++
    }
    else
    {
    stack.Push(infos[i]);
    }
    }
    return Convert.ToDouble(stack.Pop());
    }
    #endregion
    }
    }

    b.访问Web服务: 访问Web服务的客户端可以有三种方式: JavaScript中访问Web服务、基于服务的Web服务(我们用手动生成代理类的方式来访问Web服务)、基于验证方法的Web服务(我们在VS中通过”添加Web引用”或”添加服务引用”来自动生成代理类并访问Web服务)(值得注意的是: 针对第三种方式, 如果要实现异步访问, 只能在”添加符服务引用”中, 选择”高级”并勾选”生成异步操作”).

    1.)JavaScript调用Web服务

    //JsCallWebService.htm

    代码
    <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">

    <html xmlns="http://www.w3.org/1999/xhtml">
    <head>
    <title>Untitled Page</title>
    </head>
    <body>
    <body>
    <!--JavaScript调用Web服务-->
    <input id="JS_Text" type="text" />&nbsp;
    <input id="JS_cal" type="button" value="Js调Web服务" onclick="send();" />
    <hr />

    <!--JS脚本异步通信, js调用web服务是通过构造SOAP请求包来实现的. [Ajax实现的效果均可以通过WebService来实现]-->
    <script type="text/javascript" >
    //1.创建ActiveXObject对象
    var xhr;
    if(window.ActiveXObject){
    xhr
    = new ActiveXObject("Microsoft.XMLHTTP");
    }
    else if(window.XMLHttpRequest){
    xhr
    = new XMLHttpRequest();
    }
    else {
    throw new Error("This browser is not supported.");
    }

    //2.发送请求(其中包括: 设置请求方式、结果处理及发送内容)
    function send(){
    xhr.open(
    "POST","http://192.168.0.67/WebService/webservice.asmx",true); //请求方式只能为POST方式(GET方式将数据保存在URL中,而这里的SOAP包保存在HTTP协议包的包体中), 忘掉同步方式吧, 这里采用异步. 默认为true
    xhr.setRequestHeader("Content-Type","text/xml; charset=utf-8"); //设置请求头
    xhr.SetRequestHeader("SOAPAction","http://tempuri.org/Compute");
    //设置请求包体, 也就是根据Web服务中SOAP协议的示例拼出SOAP请求的Body部分(双引号换单引号)
    var data="<?xml version='1.0' encoding='utf-8'?><soap:Envelope xmlns:xsi='http://www.w3.org/2001/XMLSchema-instance' xmlns:xsd='http://www.w3.org/2001/XMLSchema' xmlns:soap='http://schemas.xmlsoap.org/soap/envelope/'><soap:Body><Compute xmlns='http://tempuri.org/'><expression>"+document.getElementById("JS_Text").value+"</expression></Compute></soap:Body></soap:Envelope>";
    xhr.onreadystatechange
    = requestHandler; //设置结果处理方法
    xhr.send(data); //发送
    }
    function requestHandler(){
    if(xhr.readyState == 4){ //返回结果接收完毕, 3表示包头接收完毕, 4表示包体接收完毕
    if(xhr.status == 200){ //200表示返回结果正常, 4XX权限问题, 5XX错误
    var pattern = new RegExp("<ComputeResult[\\w\\W]+?<\\/ComputeResult>","i"); //以下表达式中: +?表示最小匹配, 即只匹配本段而非全文; \\/是在C#中\/有可能会报错而多写一个转义符; i表示忽略大小写, g表示查找全文, gi表示查找全文并忽略大小写
    var match = pattern.exec(xhr.responseText);

    var firstmatch = match[0].replace("<ComputeResult>",""); //拆结果, 只有一个<expression>, 写上match[0], 速度会比较快
    var finalmatch = firstmatch.replace("</ComputeResult>","");
    document.getElementById(
    "JS_Text").value = document.getElementById("JS_Text").value + " = " +finalmatch;
    }
    }
    }
    </script>
    </body>
    </html>

    2.)基于服务的Web服务(即: 不带验证的Web服务), 这里通过手动添加代理类来做

    a.首先从服务器上下载Web服务描述语言(Web Services Description Language, WSDL)表示的Web服务说明文件. 只要在IE浏览器中, 输入Web服务的URL请求地址后, 再加上”?WSDL”或直接点击”服务描述”即可, 将得到的网页另存到本地, 例如: 另存为service.wsdl 额外的表明下, 若加上”?DISCO”则会得到Web服务的发现信息.

    b. 在C:\Program Files\Microsoft SDKs\Windows\v6.0A\Bin(或你的安装路径)中, 找到wsdl.exe 并复制到出来, 例如: 放到c:\temp目录下(用完可以删除). 然后, 将下载并另存的service.wdsl也放在同意文件夹temp下. 在cmd中运行c:\temp> wsdl service.dll 后便会得到代理类, 我这里得到的是calculate.cs

    c.在你的项目中, 引入这个生成的代理类calculate.cs即可, 之后c:\temp中的东西就可以删除了.

    值得注意的是: B/S的结构决定了其不能采用异步方式, 因为子线程去计算的时候, 主页面已经生成了无法再更改. 在客户端只有一种语言可以改, 就是JS, 即使是Ajax技术最后也是要使用JS的. 如果不实用JS的话B/S中是看不到效果的, 但在C/S的项目中可以直接使用异步方式. 于此类似, 在控制台项目或网站项目中, “添加Web引用”是不能生成包含异步操作的代理类的.

    //ServiceBasedWS/default.aspx

    代码
    <%@ Page Language="C#" AutoEventWireup="true" CodeFile="Default.aspx.cs" Inherits="_Default" %>

    <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">

    <html xmlns="http://www.w3.org/1999/xhtml">
    <head runat="server">
    <title>Untitled Page</title>
    </head>
    <body>
    <form id="form1" runat="server">
    <div>
    <asp:TextBox ID="service_text" runat="server"></asp:TextBox>&nbsp;
    <asp:Button ID="btn_service" runat="server" Text="基于服务的Web服务"
    onclick
    ="btn_service_Click" />
    </div>
    </form>
    </body>
    </html>

    //ServiceBasedWS/default.aspx.cs

    代码
    using System;
    using System.Configuration;
    using System.Data;
    using System.Linq;
    using System.Web;
    using System.Web.Security;
    using System.Web.UI;
    using System.Web.UI.HtmlControls;
    using System.Web.UI.WebControls;
    using System.Web.UI.WebControls.WebParts;
    using System.Xml.Linq;

    public partial class _Default : System.Web.UI.Page
    {
    protected void Page_Load(object sender, EventArgs e)
    {
    if (!this.IsPostBack)
    {
    this.service_text.Text = "0";
    }

    }
    protected void btn_service_Click(object sender, EventArgs e)
    {
    //#region B/S的结构决定了, 其不能采用异步方式, 因为子线程去计算的时候, 主页面已经生成了无法再更改. 在客户端只有一种语言可以改, 就是JS
    //Calculate cal = new Calculate();
    ////这里依然采用异步方式
    //cal.BeginCompute(this.service_text.Text.Trim(), new AsyncCallback(DealCompute), cal); //因为要开辟子线程, 需要保存代理类的引用cal
    //#endregion

    #region 同步方式
    Calculate cal
    = new Calculate();
    string result = cal.Compute(this.service_text.Text.Trim()).ToString() ;
    this.service_text.Text += " = " + result;
    #endregion
    }
    protected void DealCompute(IAsyncResult ia)
    {
    Calculate cal
    = ia.AsyncState as Calculate;
    this.service_text.Text += " = " + cal.EndCompute(ia).ToString();
    }
    }

    //ServiceBasedWS/App_Code/calculate.cs    //代理类

    代码
    //------------------------------------------------------------------------------
    // <auto-generated>
    // This code was generated by a tool.
    // Runtime Version:2.0.50727.3615
    //
    // Changes to this file may cause incorrect behavior and will be lost if
    // the code is regenerated.
    // </auto-generated>
    //------------------------------------------------------------------------------

    using System;
    using System.ComponentModel;
    using System.Diagnostics;
    using System.Web.Services;
    using System.Web.Services.Protocols;
    using System.Xml.Serialization;

    //
    // This source code was auto-generated by wsdl, Version=2.0.50727.1432.
    //


    /// <remarks/>
    [System.CodeDom.Compiler.GeneratedCodeAttribute("wsdl", "2.0.50727.1432")]
    [System.Diagnostics.DebuggerStepThroughAttribute()]
    [System.ComponentModel.DesignerCategoryAttribute(
    "code")]
    [System.Web.Services.WebServiceBindingAttribute(Name
    ="CalculateSoap", Namespace="http://tempuri.org/")]
    public partial class Calculate : System.Web.Services.Protocols.SoapHttpClientProtocol {

    private System.Threading.SendOrPostCallback ComputeOperationCompleted;

    /// <remarks/>
    public Calculate() {
    this.Url = "http://192.168.0.67/WebService/webservice.asmx";
    }

    /// <remarks/>
    public event ComputeCompletedEventHandler ComputeCompleted;

    /// <remarks/>
    [System.Web.Services.Protocols.SoapDocumentMethodAttribute("http://tempuri.org/Compute", RequestNamespace="http://tempuri.org/", ResponseNamespace="http://tempuri.org/", Use=System.Web.Services.Description.SoapBindingUse.Literal, ParameterStyle=System.Web.Services.Protocols.SoapParameterStyle.Wrapped)]
    public double Compute(string expression) {
    object[] results = this.Invoke("Compute", new object[] {
    expression});
    return ((double)(results[0]));
    }

    /// <remarks/>
    public System.IAsyncResult BeginCompute(string expression, System.AsyncCallback callback, object asyncState) {
    return this.BeginInvoke("Compute", new object[] {
    expression}, callback, asyncState);
    }

    /// <remarks/>
    public double EndCompute(System.IAsyncResult asyncResult) {
    object[] results = this.EndInvoke(asyncResult);
    return ((double)(results[0]));
    }

    /// <remarks/>
    public void ComputeAsync(string expression) {
    this.ComputeAsync(expression, null);
    }

    /// <remarks/>
    public void ComputeAsync(string expression, object userState) {
    if ((this.ComputeOperationCompleted == null)) {
    this.ComputeOperationCompleted = new System.Threading.SendOrPostCallback(this.OnComputeOperationCompleted);
    }
    this.InvokeAsync("Compute", new object[] {
    expression},
    this.ComputeOperationCompleted, userState);
    }

    private void OnComputeOperationCompleted(object arg) {
    if ((this.ComputeCompleted != null)) {
    System.Web.Services.Protocols.InvokeCompletedEventArgs invokeArgs
    = ((System.Web.Services.Protocols.InvokeCompletedEventArgs)(arg));
    this.ComputeCompleted(this, new ComputeCompletedEventArgs(invokeArgs.Results, invokeArgs.Error, invokeArgs.Cancelled, invokeArgs.UserState));
    }
    }

    /// <remarks/>
    public new void CancelAsync(object userState) {
    base.CancelAsync(userState);
    }
    }

    /// <remarks/>
    [System.CodeDom.Compiler.GeneratedCodeAttribute("wsdl", "2.0.50727.1432")]
    public delegate void ComputeCompletedEventHandler(object sender, ComputeCompletedEventArgs e);

    /// <remarks/>
    [System.CodeDom.Compiler.GeneratedCodeAttribute("wsdl", "2.0.50727.1432")]
    [System.Diagnostics.DebuggerStepThroughAttribute()]
    [System.ComponentModel.DesignerCategoryAttribute(
    "code")]
    public partial class ComputeCompletedEventArgs : System.ComponentModel.AsyncCompletedEventArgs {

    private object[] results;

    internal ComputeCompletedEventArgs(object[] results, System.Exception exception, bool cancelled, object userState) :
    base(exception, cancelled, userState) {
    this.results = results;
    }

    /// <remarks/>
    public double Result {
    get {
    this.RaiseExceptionIfNecessary();
    return ((double)(this.results[0]));
    }
    }
    }

    3.)基于验证方法的Web服务

    有时候我们并不希望自己的写的类库被任意调用, 比如: 针对商业应用开发的类库, 那么我们就需要一些带有安全措施的Web服务了. 这里简单介绍两种安全方式, 一种是基于IIS和Windows集成验证方式, 另外一种是基于SoapHeader的安全验证. 关于更加安全的方式可以采用WSE(Web Service Enhancements, 增强的Web服务), 在WCF中已经增加到WSE的支持.

    关于第一种验证采用IIS中的Windows集成验证方式, 适用于小规模的开发团队. 例如: 10人左右, 这时我们不用对Web服务的代码做任何修改, 只要在IIS服务器中创建一个名称为ShareCodeUser的用户, 大家都用这个账户来访问共享代码即可.

    具体做法: 取消IIS中”匿名访问”, 勾选”集成Windows验证”. 不要使用基本验证, 密码明文传输的安全隐患太大. 在Windows中创建ShareCodeUser账户, 尽可能赋予其较低的权限. 在服务器中找到已发布的Web服务所在的文件夹, 之后”右键” ---> “属性” ---> “安全”选项卡中, 添加ShareCodeUser用户, 之后ShareCoderUser用户就可以访问Web服务了.

    为了能够使用异步方式, 这里采用WinForm客户端且通过”添加服务引用”来添加代理类(这样能自动生成配置文件): 创建WinForm项目, 添加”Web服务引用”, 输入地址: http://192.168.0.67/WebService/webservice.asmx?WSDL 后, 点击”高级” ---> “生成异步操作”.

    这里有两个注意事项:

    第一: 要使用Windows集成验证, 需要在客户端的app.config(自动生成)中, 配置验证方式为”Windows”.

    第二: 生成的代理类中(csc)的ClientCredentials是只读属性, 我们new 的System.Net.NetworkCredential()对象需要赋值给csc.ClientCredentials.Windows.Credential           //猜想: csc.ClientCredentials只是拿到代理类中的用于验证用户的类引用, 而不是具体的验证信息.

    // WS_WinClient/Form1.cs    //采用Async方法和事件处理器实现异步

    代码
    using System;
    using System.Collections.Generic;
    using System.ComponentModel;
    using System.Data;
    using System.Drawing;
    using System.Linq;
    using System.Text;
    using System.Windows.Forms;

    namespace WS_WinClient
    {
    public partial class mainForm : Form
    {
    public mainForm()
    {
    InitializeComponent();
    }

    private void btn_cal_Click(object sender, EventArgs e)
    {
    Service.CalculateSoapClient csc
    = new WS_WinClient.Service.CalculateSoapClient();

    //为了简单起见, 这里直接硬编码账户名和密码
    System.Net.NetworkCredential nc = new System.Net.NetworkCredential("test","test");

    //这里千万别忘了, 这里需要更改配置文件中的验证方式为windows
    csc.ClientCredentials.Windows.ClientCredential = nc; //csc.ClientCredentials是只读的, 用于获取客户端验证凭据的

    //使用Async+事件完成异步
    csc.ComputeAsync(this.txt_exp.Text.Trim());
    csc.ComputeCompleted
    += new EventHandler<WS_WinClient.Service.ComputeCompletedEventArgs>(csc_ComputeCompleted);

    }

    void csc_ComputeCompleted(object sender, WS_WinClient.Service.ComputeCompletedEventArgs e)
    {
    this.txt_exp.Text += " = " + e.Result.ToString();
    }
    }
    }

    //WS_WinClient/app.config //配置文件中, 验证方式的配置节

    代码
    <security mode="TransportCredentialOnly">
    <transport clientCredentialType="Windows" proxyCredentialType="None" realm="" />
    <message clientCredentialType="UserName" algorithmSuite="Default" />
    </security>

    接下来是基于验证方法的Web服务:

    基于验证方法的Web服务将验证信息放在了Soap协议包的包头中, 即: 验证信息放在SoapHeader中, 这样每次发送的Soap请求包中, 都包含用户名和密码信息, 以此达到验证用户的目的.

    首先: 修改IIS服务器上的Web服务代码, 添加在SoapHeader添加验证信息.

    a.自定义类继承自SoapHeader, 用于承载验证信息

    b.在共享代码的类中, 添加自定义类的一个变量作为共享代码类的公共成员, 用于在服务器端接收 客户端通过Soap协议头传递的参数, 即用户名和密码信息.

    c.在希望取得验证信息的服务类的方法上使用[System.Web.Services.Protocols.SoapHeader(“变量“, Direction=SoapHeaderDirection.InOut)]标签, 并且在共享的方法内添加验证处理代码. 改SoapHeader标签的作用就是从Soap请求包头中, 将用户名和密码等信息放到b中提到的变量中.

    修改后的服务器端代码, //WebService. webservice.asmx

    <%@ WebService Language="C#" CodeBehind="~/App_Code/Service.cs" Class="Service" %>

    //WebService/App_Code/Service.cs

    代码
    using System;
    using System.Linq;
    using System.Web;
    using System.Web.Services;
    using System.Web.Services.Protocols;
    using System.Xml.Linq;

    using System.Collections.Generic;

    [WebService(Namespace
    = "http://tempuri.org/")]
    [WebServiceBinding(ConformsTo
    = WsiProfiles.BasicProfile1_1)]
    // To allow this Web Service to be called from script, using ASP.NET AJAX, uncomment the following line.
    // [System.Web.Script.Services.ScriptService]
    public class Service : System.Web.Services.WebService
    {
    public Service () {

    //Uncomment the following line if using designed components
    //InitializeComponent();
    }

    public UserValidateInfo userinfo; //用来保存客户端传递过来的用户名和密码参数
    [System.Web.Services.WebMethod]
    [SoapHeader(
    "userinfo",Direction=SoapHeaderDirection.InOut)] //SoapHeader属性的目的是从Soap协议头中取出用户名和密码等信息后, 放到上边的userinfo中
    public double Compute(string expression)
    {
    if (userinfo.UserName == "Test" && userinfo.UserPass == "TEST")
    {
    //处理最开始的-或者+号
    if (expression.StartsWith("+") || expression.StartsWith("-"))
    {
    expression
    = "0" + expression;
    }

    //1. 将+、-、*、/分离成出来
    expression = expression.Replace("+", ",+,");
    expression
    = expression.Replace("-", ",-,");
    expression
    = expression.Replace("*", ",*,");
    expression
    = expression.Replace("/", ",/,");

    string[] infos = expression.Split(',');
    Stack
    <string> stack = new Stack<string>(); //使用list模拟堆栈

    //2. 利用堆栈计算乘除的结果
    double prenum;
    double nextnum;
    for (int i = 0; i < infos.Length; i++)
    {
    if (infos[i] == "*" || infos[i] == "/")
    {
    prenum
    = Convert.ToDouble(stack.Pop());
    nextnum
    = Convert.ToDouble(infos[i + 1]);
    if (infos[i] == "*")
    {
    stack.Push((prenum
    * nextnum).ToString());
    }
    else
    {
    stack.Push((prenum
    / nextnum).ToString());
    }
    i
    ++; //别忘了i要++
    }
    else
    {
    stack.Push(infos[i]);
    }
    }

    //3. 利用堆栈计算加减的结果

    //infos = stack.ToArray(); //将stack转存到数组中, 这里转存的结果是逆序的
    infos = new string[stack.Count];
    for (int i = infos.Length - 1; i >= 0; i--) //将stack正序转存到数组中
    {
    infos[i]
    = stack.Pop();
    }

    prenum
    = 0; //重置prenum和nextnum
    nextnum = 0;
    for (int i = 0; i < infos.Length; i++)
    {
    if (infos[i] == "+" || infos[i] == "-")
    {
    prenum
    = Convert.ToDouble(stack.Pop());
    nextnum
    = Convert.ToDouble(infos[i + 1]);
    if (infos[i] == "+")
    {
    stack.Push((prenum
    + nextnum).ToString());
    }
    else
    {
    stack.Push((prenum
    - nextnum).ToString());
    }
    i
    ++; //别忘了i++
    }
    else
    {
    stack.Push(infos[i]);
    }
    }
    return Convert.ToDouble(stack.Pop());
    }
    else
    {
    return 0;
    }
    }

    }

    //WebService/App_Code/ UserValidateInfo.cs


    public class UserValidateInfo : System.Web.Services.Protocols.SoapHeader
    {
    public string UserName;
    public string UserPass;
    }

    客户端代码有点小区别: 如果将验证类写在共享代码的cs文件中, 则生成的代理类中有”自定义类+Value”命名的属性, 验证信息只要赋给该属性即可; 若做成单独的验证类(不在共享代码的cs文件中), 则生成的代理类中只有”自定义类”, 这时, 验证过程需要new 一个自定义类的对象并对该对象的成员赋值, 之后将该对象通过参数列表传递给方法, 从而达到验证效果.

    基于验证方法的客户端代码, //WS_SoapHeader_Client/Form1.cs

    代码
    using System;
    using System.Collections.Generic;
    using System.ComponentModel;
    using System.Data;
    using System.Drawing;
    using System.Linq;
    using System.Text;
    using System.Windows.Forms;

    namespace WS_SoapHeader_Client
    {
    public partial class mainForm : Form
    {
    public mainForm()
    {
    InitializeComponent();
    }

    private void btn_cal_Click(object sender, EventArgs e)
    {
    WService.ServiceSoapClient ssc
    = new WS_SoapHeader_Client.WService.ServiceSoapClient();

    WService.UserValidateInfo uvi
    = new WS_SoapHeader_Client.WService.UserValidateInfo();
    uvi.UserName
    = "Test";
    uvi.UserPass
    = "TEST";

    //先注册事件, 在开始异步处理比较好. 防止异步执行结束, 事件还没有注册上
    ssc.ComputeCompleted += new EventHandler<WS_SoapHeader_Client.WService.ComputeCompletedEventArgs>(ssc_ComputeCompleted);
    ssc.ComputeAsync(uvi,
    this.txt_cal.Text.Trim(), ssc);

    }

    void ssc_ComputeCompleted(object sender, WS_SoapHeader_Client.WService.ComputeCompletedEventArgs e)
    {
    this.txt_cal.Text += " = " + e.ComputeResult.ToString();
    }
    }
    }
  • 相关阅读:
    [linux] 内存检测
    [思维]牛客编程巅峰赛S1第6场
    [逆序数, 思维]牛客编程巅峰赛S1第5场
    [Linux] TrafficControl 流量控制
    [linux]常用指令
    [带权并查集]小白月赛25-C 白魔法师
    初窥原型
    性能测试流程
    JMeter服务器监控技术
    使用Fiddler进行HTTPS抓包和手机APP抓包
  • 原文地址:https://www.cnblogs.com/cs_net/p/1923974.html
Copyright © 2020-2023  润新知