Поделиться через


Пошаговое руководство. Использование транспорта по протоколу HTTP

Обновлен: Ноябрь 2007

Можно использовать транспорт по протоколу HTTP для создания приложения для устройства, которое будет подсоединяться к службе Windows Communication Foundation (WCF) персонального компьютера.

В этом разделе описываются конфигурация службы WCF для обработки подключений устройств и способы создания пользовательских приложений. Объясняются отличия в конфигурации службы WCF и клиентского программирования, которые следует учитывать, чтобы мобильное устройство могло подсоединиться к службе. Дополнительные сведения о создании приложений WCF для персонального компьютера см. в разделе Getting Started Tutorial в документации WCF.

Чтобы создать службу WCF для персонального компьютера

  1. Создайте новый проект веб-службы.

  2. Измените файл Web.config как показано в следующем примере. Измените следующие элементы и атрибуты в файле:

    • Измените значение атрибута <endpoint>binding на "basicHttpBindin". .NET Compact Framework поддерживает шифрование текста, но не двоичное шифрование.

    • Измените значение атрибута behaviorConfiguration, чтобы он ссылался на новое имя поведения.

    • Замените элемент <behavior>, как показано в примере.

    • Если нужно зарегистрировать обработчик службы HTTP в файле Web.config, добавьте новый элемент <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.

    Bb397805.alert_note(ru-ru,VS.90).gifПримечание.

    Пример не реализует поддержку для параметров, указанных в контрактах, таких, как ServiceContract и OperationContract. Если нужна поддержка параметра для этих контактов, чтобы создать код клиента, можно использовать средство ServiceModel Utility для WCF платформы .NET Compact Framework (NetCFSvcUtil.exe). Этот инструмент встраивает поддержку для многих из этих параметров в приложения, основанные на .NET Compact Framework. NetCFSvcUtil.exe доступен в Power Toys для .NET Compact Framework. Дополнительные сведения см. в разделе Power Toys для .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-службы на вашем веб-сервере.

    Используйте любой порт между 10000 и 650000, например, 50505.

  5. Запустите веб-сервер.

    Для просмотра вывода языка описания веб-служб (WSDL) и запуска службы на локальном узле, выберите https://localhost:50505/CalculatorService/Service.svc?wsdl. Используйте тот же номер порта и имя веб-проекта, которые Вы указали для службы WCF.

  6. Если Вы планируете подсоединиться к веб-серверу с удаленного компьютера или устройства, создайте виртуальную папку, ссылающуюся на папку, содержащую веб-проект.

    Bb397805.alert_note(ru-ru,VS.90).gifПримечание.

    Сервер разработки ASP.NET в Visual Studio отвечает только на запросы с вашей локальной машины. Рекомендуется использовать службы IIS для создания виртуальных папок. Это позволит Вам подсоединяться к веб-серверу с удаленного устройства всегда, когда сервер доступен.

  7. Проверьте, что можно получить доступ к директории из обозревателя персонального компьютера и из обозревателя устройства.

Чтобы создать клиент .NET Compact Framework

  1. Во время выполнения службы откройте командную строку и перейдите в каталог, в котором расположена служба WCF.

  2. Из командной строки запустите средство ServiceModel Desktop Utility для WCF (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. В сгенерированном коде прокси добавьте реализации метода путем вызова метода Call в определенном пользователем классе ClientBase.

    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.

    Bb397805.alert_note(ru-ru,VS.90).gifПримечание.

    В этом примере класс CustomBodyWriter в ClientBase поддерживает только примитивные типы. Для поддержки не примитивных типов необходимо расширить метод 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 и .NET Compact Framework