Дуплексный сервис WCF - прямой вызов и обратный вызов не используют один и тот же канал - PullRequest
0 голосов
/ 29 июля 2010

Моя цель - получить службу WCF за брандмауэром без открытых портов. Решение, которое я выбрал, заключается в размещении дуплексной службы WCF на общедоступной стороне, которая имеет в качестве обратного вызова тот же контракт, который использовался, если брандмауэр не использовался.

Это сработало, если я использовал netTcpBinding, но так как мне нужно потоковое соединение, мне пришлось использовать пользовательскую привязку.

Все работает нормально, пока я не подниму брандмауэр. В этот момент прямой вызов (из-за брандмауэра) работает нормально, но обратный вызов - нет (брандмауэр останавливает его).

Вопрос в том, ПОЧЕМУ? Разве они не используют тот же канал, что и для предопределенного netTcpBinding?

Вот мой стек каналов в app.config:

<customBinding>
  <binding name="ReversedServiceBinding">
    <compositeDuplex />
    <oneWay />
    <binaryMessageEncoding />
    <tcpTransport transferMode="Streamed" />
  </binding>
</customBinding>

1 Ответ

2 голосов
/ 09 августа 2010

Фактическое поведение правильное.Вы использовали связующий элемент CompositeDuplex - значение составного слова - именно то, что вы получаете - два отдельных канала, каждый из которых работает в одном направлении.Композитный дуплекс необходим только для транспортного канала, который по умолчанию не поддерживает дуплексную связь.Это не относится к транспортному каналу TCP - проверьте примечания .Если вы не используете CompositeDuplex, он должен работать так, как вы ожидали.Также нет необходимости определять совершенно новую пользовательскую привязку, чтобы разрешить потоковую передачу. NetTcpBinding также имеет свойство TransportMode, которое можно указать в конфигурации.

РЕДАКТИРОВАТЬ: Я подготовил рабочий пример с дуплексной связью Net.TCP.Это не использует потоковую передачу.Вы правы, что потоковая передача невозможна при использовании дуплексной связи по протоколу TCP.Вы можете попытаться объединить дуплексную связь с каналом чанкинга или проверить Расширения WCF (коммерческий продукт).

Общие контракты

namespace NetTcpDuplexContracts
{
    [ServiceContract(SessionMode = SessionMode.Required, 
        CallbackContract = typeof(IDuplexServiceCallback))]
    public interface IDuplexService
    {
        [OperationContract(IsOneWay = true)]
        void DoAction(string message);
    }

    public interface IDuplexServiceCallback
    {
        [OperationContract(IsOneWay = true)]
        void ConfirmAction(string message);
    }
}

Сервис и хост

namespace NetTcpDuplexService
{
    public class DuplexService : IDuplexService
    {
        public void DoAction(string message)
        {
            Console.WriteLine("DoAction: " + message);

            var callbackChannel = 
                OperationContext.Current.GetCallbackChannel<IDuplexServiceCallback>();
            callbackChannel.ConfirmAction("Ping back " + message);
        }
    }

    class Program
    {
        public static void Main(string[] args)
        {
            try
            {
                using (var host = new ServiceHost(typeof(DuplexService)))
                {
                    host.Open();

                    Console.ReadLine();
                }
            }
            catch (Exception e)
            {
                Console.WriteLine(e.Message);
                Console.ReadLine();
            }
        }
    }
}

Конфигурация сервиса

<configuration>
  <system.serviceModel>
    <services>
      <service name="NetTcpDuplexService.DuplexService">
        <host>
          <baseAddresses>
            <add baseAddress="net.tcp://localhost:8800/NetTcpDuplexService"/>
          </baseAddresses>
        </host>
        <endpoint address="" binding="netTcpBinding" contract="NetTcpDuplexContracts.IDuplexService" />
      </service> 
    </services>
  </system.serviceModel>
</configuration>

Обратный звонок и клиент

namespace NetTcpDuplexClient
{
    public class DuplexServiceCallback : IDuplexServiceCallback
    {
        public void ConfirmAction(string message)
        {
            Console.WriteLine(message);
        }
    }

    class Program
    {
        public static void Main(string[] args)
        {
            DuplexChannelFactory<IDuplexService> factory = null;

            try
            {
                var callbackService = new DuplexServiceCallback();
                var context = new InstanceContext(callbackService);

                factory = new DuplexChannelFactory<IDuplexService>(context, "IDuplexService_NetTcp");
                var channel = factory.CreateChannel();
                channel.DoAction("Hello world");

                factory.Close();
                Console.ReadLine();
            }
            catch (Exception e)
            {
                if (factory != null && factory.State != CommunicationState.Closed)
                {
                    factory.Abort();
                }

                Console.WriteLine(e.Message);
                Console.ReadLine();
            }
        }
    }
}

Клиентская конфигурация

<configuration>
  <system.serviceModel>
    <client>
      <endpoint name="IDuplexService_NetTcp" address="net.tcp://localhost:8800/NetTcpDuplexService" binding="netTcpBinding"
                contract="NetTcpDuplexContracts.IDuplexService" />
    </client>
  </system.serviceModel>
</configuration>
...