如何:使用 HTTP 传输

更新:2007 年 11 月

可以使用 HTTP 传输在设备上创建将连接至台式机上的 Windows Communication Foundation (WCF) 服务的应用程序。

本主题描述如何通过配置 WCF 服务来处理连接设备,以及如何创建客户端应用程序。它说明了在使移动设备连接至 WCF 服务时,必须注意的 WCF 服务配置与编写客户端代码的区别。有关如何为台式机创建 WCF 应用程序的其他信息,请参见 WCF 文档中的入门教程

为台式机创建 WCF 服务

  1. 创建新的 Web 服务项目。

  2. 按以下示例所示修改 Web.config 文件。修改该文件中的下列元素和属性:

    • 将 <endpoint>binding 属性值更改为“basicHttpBinding”。.NET Compact Framework 支持文本编码,但不支持二进制编码。

    • 将 behaviorConfiguration 属性更改为引用新行为名称。

    • 按该示例所示替换 <behavior> 元素。

    • 如果需要在 Web.config 文件中注册 HTTP 服务处理程序,请添加一个具有该示例中所示之信息的新 <system.WebServer> 元素。

    <?xml version="1.0"?>
    
    <configuration xmlns="https://schemas.microsoft.com/.NetConfiguration/v2.0">
      <system.serviceModel>
        <services>
          <service name="CalculatorService" behaviorConfiguration="MyServiceTypeBehaviors">
            <endpoint contract="ICalculatorService" binding="basicHttpBinding"/>
          </service>
        </services>
        <behaviors>
          <serviceBehaviors>
            <behavior name="MyServiceTypeBehaviors">
              <serviceMetadata httpGetEnabled="true" />
              <serviceDebug httpHelpPageEnabled="true" includeExceptionDetailInFaults="true" />
            </behavior>
          </serviceBehaviors>
        </behaviors>
      </system.serviceModel>
    
      <system.web>
        <compilation debug="true"/>
      </system.web>
    
      <system.webServer>
        <handlers>
          <add name="HttpSvcHandler" path="*.svc" verb="*" type="System.ServiceModel.Activation.HttpHandler, System.ServiceModel, Version=3.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" resourceType="Unspecified" />
        </handlers>
      </system.webServer>
    
    </configuration>
    
  3. 在 WCF 服务的源代码中,从您的代码中移除在 ServiceContract 和 OperationContract 属性中指定的任何参数。

    说明:

    此示例未实现对协定(如 ServiceContract 和 OperationContract)中所指定的参数的支持。如果需要对这些协定的参数支持,可以使用 WCF .NET Compact Framework ServiceModel 实用工具 (NetCFSvcUtil.exe) 生成客户端代码。此工具将在基于 .NET Compact Framework 的应用程序中生成对其中许多参数的支持。Power Toys for .NET Compact Framework 中提供了 NetCFSvcUtil.exe。有关更多信息,请参见 Power Toys for .NET Compact Framework

    下面的示例演示一个简化的计算器应用程序的 WCF 服务源代码。

    <ServiceContract()>  _
    Public Interface ICalculatorService
        <OperationContract()>  _
        Function Add(ByVal n1 As Double, ByVal n2 As Double) As Double 
        '<OperationContract()>  _
        Function Subtract(ByVal n1 As Double, ByVal n2 As Double) As Double
    End Interface
    
    
    Public Class CalculatorService
        Implements ICalculatorService
    
        Public Function Add(ByVal n1 As Double, ByVal n2 As Double) As Double Implements ICalculatorService.Add
            Return n1 + n2
    
        End Function
    
        Public Function Subtract(ByVal n1 As Double, ByVal n2 As Double) As Double Implements ICalculatorService.Subtract
            Return n1 - n2
    
        End Function
    End Class
    
    [ServiceContract()]
    public interface ICalculatorService
    {
        [OperationContract]
        double Add(double n1, double n2);
        [OperationContract]
        double Subtract(double n1, double n2);
    }
    
    public class CalculatorService : ICalculatorService
    {
        public double Add(double n1, double n2) { return n1 + n2; }
        public double Subtract(double n1, double n2) { return n1 - n2; }
    }
    
  4. 将 WCF 服务设置为使用 Web 服务器上的特定端口。

    可以使用介于 10000 和 650000 之间的任意端口号。此示例使用端口 50505。

  5. 启动 Web 服务器。

    如果您希望查看 Web 服务描述语言 (WSDL) 输出并在本地主机上运行服务,请浏览至 https://localhost:50505/CalculatorService/Service.svc?wsdl。请使用您为 WCF 服务指定的端口号和 Web 项目名称。

  6. 如果计划从远程计算机或设备连接至 Web 服务器,请设置一个虚拟目录并使之指向包含 Web 项目的目录。

    说明:

    Visual Studio 中的 ASP.NET Development Server 只响应来自本地开发计算机的请求。我们建议使用 Internet 信息服务 (IIS) 指定虚拟目录。这样,只要 Web 服务器是可以访问的,就可以从远程设备连接至该服务器。

  7. 验证是否可以通过台式机浏览器和设备浏览器访问该目录。

创建 .NET Compact Framework 客户端

  1. 在服务运行时,打开命令行并导航到 WCF 服务所在的目录。

  2. 从命令行运行 WCF ServiceModel Desktop 实用工具 (SvcUtil.exe) 以生成 WCF 客户端代理。下面是一个从命令行调用 SvcUtil 的示例,其中服务寄宿在本地主机上:

    svcutil.exe /language:c# https://localhost:50505/CalculatorService/Service.svc
    

    下面的示例演示 SvcUtil 基于简单的计算器示例生成的客户端代理。

    //------------------------------------------------------------------------------
    // <auto-generated>
    //     This code was generated by a tool.
    //     Runtime Version:2.0.50727.42
    //
    //     Changes to this file may cause incorrect behavior and will be lost if
    //     the code is regenerated.
    // </auto-generated>
    //------------------------------------------------------------------------------
    
    
    
    [System.CodeDom.Compiler.GeneratedCodeAttribute("System.ServiceModel", "3.0.0.0")]
    [System.ServiceModel.ServiceContractAttribute(ConfigurationName="ICalculatorService")]
    public interface ICalculatorService
    {
    
        [System.ServiceModel.OperationContractAttribute(Action="https://fabrikam.com/ICalculatorService/Add", ReplyAction="https://fabrikam.com/ICalculatorService/AddResponse")]
        double Add(double n1, double n2);
    
        [System.ServiceModel.OperationContractAttribute(Action="https://fabrikam.com/ICalculatorService/Subtract", ReplyAction="https://fabrikam.com/ICalculatorService/SubtractResponse")]
        double Subtract(double n1, double n2);
    }
    
    [System.CodeDom.Compiler.GeneratedCodeAttribute("System.ServiceModel", "3.0.0.0")]
    public interface ICalculatorServiceChannel : ICalculatorService, System.ServiceModel.IClientChannel
    {
    }
    
    [System.Diagnostics.DebuggerStepThroughAttribute()]
    [System.CodeDom.Compiler.GeneratedCodeAttribute("System.ServiceModel", "3.0.0.0")]
    public partial class CalculatorServiceClient : System.ServiceModel.ClientBase<ICalculatorService>, ICalculatorService
    {
    
        public CalculatorServiceClient()
        {
        }
    
        public CalculatorServiceClient(string endpointConfigurationName) : 
                base(endpointConfigurationName)
        {
        }
    
        public CalculatorServiceClient(string endpointConfigurationName, string remoteAddress) : 
                base(endpointConfigurationName, remoteAddress)
        {
        }
    
        public CalculatorServiceClient(string endpointConfigurationName, System.ServiceModel.EndpointAddress remoteAddress) : 
                base(endpointConfigurationName, remoteAddress)
        {
        }
    
        public CalculatorServiceClient(System.ServiceModel.Channels.Binding binding, System.ServiceModel.EndpointAddress remoteAddress) : 
                base(binding, remoteAddress)
        {
        }
    
        public double Add(double n1, double n2)
        {
            return base.Channel.Add(n1, n2);
        }
    
        public double Subtract(double n1, double n2)
        {
            return base.Channel.Subtract(n1, n2);
        }
    }
    
  3. 从生成的客户端代理代码中移除不受支持的属性和元素,包括:

    • 所有的 System.ServiceModel 属性。

    • IClientChannel 类的引用。

    • 对 <endpoint> 配置名称的引用。

    • 在内部通道上调用 ServiceContract 接口方法的方法实现。

    下面的示例演示经过这些修改后的代码。

    '------------------------------------------------------------------------------
    ' <auto-generated>
    '     This code was generated by a tool.
    '     Runtime Version:2.0.50727.312
    '
    '     Changes to this file may cause incorrect behavior and will be lost if
    '     the code is regenerated.
    ' </auto-generated>
    '------------------------------------------------------------------------------
    
    Option Strict Off
    Option Explicit On
    
    
    
    Public Interface ICalculatorService
    
        Function Add(ByVal n1 As Double, ByVal n2 As Double) As Double
    
        Function Subtract(ByVal n1 As Double, ByVal n2 As Double) As Double
    End Interface
    
    Partial Public Class CalculatorServiceClient
        Inherits System.ServiceModel.ClientBase(Of ICalculatorService)
        Implements ICalculatorService
    
        ' Add a variable containing the endpoint address.
        Public Shared ServiceEndPoint As New System.ServiceModel.EndpointAddress("https://fabrikam.com/CalcService/CalculatorService/Service.svc")
    
        Public Sub New(ByVal binding As System.ServiceModel.Channels.Binding, ByVal remoteAddress As System.ServiceModel.EndpointAddress)
            MyBase.New(binding, remoteAddress)
        End Sub
    
        Public Function Add(ByVal n1 As Double, ByVal n2 As Double) As Double Implements ICalculatorService.Add
            Return MyBase.Channel.Add(n1, n2)
        End Function
    
        Public Function Subtract(ByVal n1 As Double, ByVal n2 As Double) As Double Implements ICalculatorService.Subtract
            Return MyBase.Channel.Subtract(n1, n2)
        End Function
    End Class
    
    //------------------------------------------------------------------------------
    // <auto-generated>
    //     This code was generated by a tool.
    //     Runtime Version:2.0.50727.42
    //
    //     Changes to this file may cause incorrect behavior and will be lost if
    //     the code is regenerated.
    // </auto-generated>
    //------------------------------------------------------------------------------
    
    
    public interface ICalculatorService
    {
    
        double Add(double n1, double n2);
    
        double Subtract(double n1, double n2);
    }
    
    public partial class CalculatorServiceClient : System.ServiceModel.ClientBase<ICalculatorService>, ICalculatorService
    {
    
        // Add a variable to specify the server address.
        public static System.ServiceModel.EndpointAddress ServiceEndPoint = new System.ServiceModel.EndpointAddress("https://fabrikam.com/CalcService/CalculatorService/Service.svc");
    
        public CalculatorServiceClient(System.ServiceModel.Channels.Binding binding, System.ServiceModel.EndpointAddress remoteAddress) : 
                base(binding, remoteAddress)
        {
        }
    
        public double Add(double n1, double n2)
        {
            return base.Channel.Add(n1, n2);
        }
    
        public double Subtract(double n1, double n2)
        {
            return base.Channel.Subtract(n1, n2);
        }
    }
    
  4. 创建一个客户端项目。

  5. 将生成的客户端代理添加到该项目中。

  6. 在生成的代理代码中,将对 ClientBase<TChannel> 的完全限定引用更改为用户定义的 ClientBase 类。

  7. 在生成的代理代码中,通过调用用户定义的 ClientBase 类中的 Call 方法来添加方法实现。

    Public Function Add(ByVal n1 As Double, ByVal n2 As Double) As Double Implements ICalculatorService.Add
        Return System.Convert.ToDouble(MyBase.Call("Add", "https://fabrikam.com/CalcService/ICalculatorService/Add", New String() {"n1", "n2"}, New Object() {n1, n2}, GetType(Double)))
    End Function
    
    public double Add(double n1, double n2)
    {
        return (double)base.Call("Add", "https://fabrikam.com/CalcService/ICalculatorService/Add", new string[] { "n1", "n2" }, new object[] { n1, n2 }, typeof(double));
    }
    
  8. 将代理的基类添加到该项目中。此类名为 ClientBase。

    将客户端代理的基类引用更改为指向您的 ClientBase 实现。

    说明:

    在本示例中,ClientBase 中的 CustomBodyWriter 类仅支持基元类型。若要支持非基元类型,您必须扩展 OnWriteBodyContents 方法。例如,可以调用自定义序列化程序来序列化消息数据。在本例中,您要将所生成的客户端代理中的代码属性转换为 XML 序列化程序可以使用的属性。在这种情况下,当您运行 SvcUtil 时必须先添加以下开关:/serializer:xmlserializer http://终结点。

    下面的代码演示了 ClientBase 类的一个示例。

    Public Class ClientBase(Of TChannel As Class)
    
        Private requestChannel As IRequestChannel
        Private messageVersion As MessageVersion
    
    
        Public Sub New(ByVal binding As System.ServiceModel.Channels.Binding, ByVal remoteAddress As System.ServiceModel.EndpointAddress)
            'this.remoteAddress = remoteAddress;
            Me.messageVersion = binding.MessageVersion
    
            Dim channelFactory As IChannelFactory(Of IRequestChannel)
            channelFactory = binding.BuildChannelFactory(Of IRequestChannel)(New BindingParameterCollection())
            channelFactory.Open()
            Me.requestChannel = channelFactory.CreateChannel(remoteAddress)
    
        End Sub
    
    
        Public Function [Call](ByVal op As String, ByVal action As String, ByVal varnames() As String, ByVal varvals() As Object, ByVal returntype As Type) As Object
            requestChannel.Open(TimeSpan.MaxValue)
    
            Dim msg As Message = Message.CreateMessage(Me.messageVersion, action, New CustomBodyWriter(op, varnames, varvals, "<ns passed in from Proxy>"))
    
            Dim reply As Message = requestChannel.Request(msg, TimeSpan.MaxValue)
            Dim reader As XmlDictionaryReader = reply.GetReaderAtBodyContents()
            reader.ReadToFollowing(op + "Result")
            Return reader.ReadElementContentAs(returntype, Nothing)
    
        End Function
    End Class
    
    
    Friend Class CustomBodyWriter
        Inherits BodyWriter
        Private op As String
        Private varnames() As String
        Private varvals() As Object
        Private ns As String
    
    
        Public Sub New(ByVal op As String, ByVal varnames() As String, ByVal varvals() As Object, ByVal ns As String)
            MyBase.New(True)
            Me.op = op
            Me.varnames = varnames
            Me.varvals = varvals
            Me.ns = ns
    
        End Sub
    
    
        Protected Overrides Sub OnWriteBodyContents(ByVal writer As XmlDictionaryWriter)
            writer.WriteStartElement(op, ns)
            Dim i As Integer
            For i = 0 To varnames.Length
                writer.WriteElementString(varnames(i), varvals(i).ToString())
            Next i
            writer.WriteEndElement()
    
        End Sub
    End Class
    
    public class ClientBase<TChannel>
        where TChannel : class
    {
        private IRequestChannel requestChannel;
        private MessageVersion messageVersion;
    
        public ClientBase(System.ServiceModel.Channels.Binding binding, System.ServiceModel.EndpointAddress remoteAddress)
        {
            //this.remoteAddress = remoteAddress;
            this.messageVersion = binding.MessageVersion;
    
            IChannelFactory<IRequestChannel> channelFactory = binding.BuildChannelFactory<IRequestChannel>(
                new BindingParameterCollection());
            channelFactory.Open();
            this.requestChannel = channelFactory.CreateChannel(remoteAddress);
        }
    
        public object Call(string op, string action, string[] varnames, object[] varvals, Type returntype)
        {
            requestChannel.Open(TimeSpan.MaxValue);
    
            //Message msg =                     
            //Message.CreateMessage(MessageVersion.<FromBinding>,
            //      action,
            //      new CustomBodyWriter(op, varnames, varvals,                 
            //"<ns passed in from Proxy>"));
    
            Message msg =                   
            Message.CreateMessage(this.messageVersion, action,
                  new CustomBodyWriter(op, varnames, varvals,               
            "<ns passed in from Proxy>"));
    
            Message reply = requestChannel.Request(msg, TimeSpan.MaxValue);
            XmlDictionaryReader reader = reply.GetReaderAtBodyContents();
            reader.ReadToFollowing(op + "Result");
            return reader.ReadElementContentAs(returntype, null);
        }
    
    }
    
    internal class CustomBodyWriter : BodyWriter
    {
        private string op;
        private string[] varnames;
        private object[] varvals;
        private string ns;
    
        public CustomBodyWriter(string op, string[] varnames, object[] varvals, string ns)
            : base(true)
        {
            this.op = op;
            this.varnames = varnames;
            this.varvals = varvals;
            this.ns = ns;
        }
    
        protected override void OnWriteBodyContents(XmlDictionaryWriter writer)
        {
            writer.WriteStartElement(op, ns);
            for (int i = 0; i < varnames.Length; i++)
                writer.WriteElementString(varnames[i], varvals[i].ToString());
            writer.WriteEndElement();
        }
    }
    
  9. 添加一个要进行实例化并使用客户端代理的类。

    下面的示例演示调用客户端代理的代码。

    Shared Sub Main(ByVal args() As String)
        Dim serverAddress As String = CalculatorServiceClient.ServiceEndPoint.Uri.AbsoluteUri
        ' Using basic http connection. WS binding should be also available.
        Dim proxy As ICalculatorService = New CalculatorServiceClient(New BasicHttpBinding, New EndpointAddress(serverAddress))
        MessageBox.Show("Add 3 + 6...")
        MessageBox.Show(proxy.Add(3, 6).ToString)
        MessageBox.Show("Subtract 8 - 3...")
        MessageBox.Show(proxy.Subtract(8, 3).ToString)
    End Sub
    
    static void Main()
    {
        string serverAddress = CalculatorServiceClient.ServiceEndPoint.Uri.AbsoluteUri;
        // Using basic http connection. WS binding should be also available.
        ICalculatorService proxy = new CalculatorServiceClient(new BasicHttpBinding(), new EndpointAddress(serverAddress));
    
    
        MessageBox.Show("Add 3 + 6...");
        MessageBox.Show((proxy.Add(3, 6)).ToString());
        MessageBox.Show("Subtract 8 - 3...");        
        MessageBox.Show((proxy.Subtract(8, 3)).ToString());
    
    }
    
  10. 生成客户端应用程序并将其部署到您的设备中。

  11. 当 WCF 服务正在运行并且您的设备已连接到网络时,在设备上启动客户端应用程序。

编译代码

WCF 服务的源代码需要引用下列命名空间:

ClientBase 类的源代码需要引用下列命名空间:

客户端应用程序中包含 Main 方法的类的源代码需要引用下列命名空间:

安全性

此示例未实现任何 WCF 安全功能。有关受支持的安全功能的更多信息,请参见 .NET Compact Framework 中的消息处理

请参见

概念

.NET Compact Framework 中的消息处理

其他资源

Windows Communication Foundation (WCF) 开发与 .NET Compact Framework