views:

90

answers:

1

Hi,

My goal is to reach a WCF service behind a firewall with no incoming ports opened. The solution I chose is to host a duplex WCF service on the public side, that has as callback the same contract that was used if no firewall was involved.

It worked if I used netTcpBinding but since I need streamed communication I had to use the custom binding.

Everything works fine until I raise up the firewall. At that point, the direct call (from behind the firewall out) works fine, but the callback does not (firewall stops it).

The question is WHY? Shoudn't they use the same channel as for the predefined netTcpBinding?

Here is my channels stack in app.config:

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

Hello,

actual behavior is correct. You have used compositeDuplex binding element - the meaning of composite word is exactly what you get - two separate channels each working for one direction. Composite duplex is needed only for transport channel which doesn't support duplex communication by default. That is not the case for TCP transport channel - check remarks. If you don't use compositeDuplex it should work like you have expected. Also there is no need to define whole new custom binding to allow streamed transport. NetTcpBinding also has TransportMode property which can be specified in configuration.

EDIT: I have prepared working example with Net.TCP duplex communication. It doesn't use streaming. You are right that streaming is not possible when duplex TCP communication is used. You can try to combine duplex communication with chunking channel or check WCF Xtensions (commercial product).

Shared contracts

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);
    }
}

Service and host

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();
            }
        }
    }
}

Service configuration

<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>

Callback and client

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();
            }
        }
    }
}

Client configration

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

Best regards, Ladislav

Ladislav Mrnka
At the link you've provided, MSDN guys say the element should be specified only for transport protocols that do not support duplex communication natively, which implies that two channels will be created, one for each communication direction. On the other hand, if I don't specify the <compositeDuplex> node in the channels stack I'm not able to host a duplex service (I get an exception saying that the channels stack is badly configured for duplex communication).So, I'm not convinced that the reason two channels are used instead of one is because I've specified <compositeDuplex>.
Gicanu
If you could provide the exact channel stack that works for a duplex communication over TCP transport without the <compositeDuplex> node, that will prove you are 100% right, and your post will be the answer.I will mark your post as the answer anyway, if nobody comes with anything else for a few days.
Gicanu
And about the last sentence in your post, the default duplex communication over netTcpBinding only works for the buffered transport mode.
Gicanu
I have added example which does not use composite channel. I checked in procmon and it didn't open second connection to the client.
Ladislav Mrnka