2

I am working on a .NET application which would enable pairs of computers located anywhere on the Internet (and usually behind NATs) to exchange messages between each other in parallel. I did some research for possible solutions and came to the conlsuion that maybe WCF duplex P2P connectivity would be suitable for what I need. Since I am new to WCF, I created a small test application and it seems to work well in general with one exception - peer which sends particular message to the other side also gets a call on its own interface and receives it back. Here is the code

using System;
using System.Threading.Tasks;
using System.ServiceModel;
using System.ServiceModel.Channels;
using System.ServiceModel.Description;

[ServiceContract(CallbackContract = typeof(IDataChannel))]
public interface IDataChannel
{
    [OperationContract(IsOneWay = true)]
    void SendData(string data);
}

public class WCFP2PTransport : IDataChannel
{
    private IDataChannel m_outChannel = null;
    private DuplexChannelFactory<IDataChannel> m_factory = null;
    private Task m_completion;
    private string m_name;

    public WCFP2PTransport(string service, string name)
    {
        m_name = name;
        try
        {
            var binding = new NetPeerTcpBinding();
            binding.Security.Mode = SecurityMode.None;

            var endpoint = new ServiceEndpoint(
                ContractDescription.GetContract(typeof(IDataChannel)),
                binding,
                new EndpointAddress("net.p2p://" + service));

            m_factory = new DuplexChannelFactory<IDataChannel>(
                new InstanceContext(this),
                endpoint);

            m_outChannel = m_factory.CreateChannel();

            m_completion = Task.Factory.FromAsync(((ICommunicationObject)m_outChannel).BeginOpen, ((ICommunicationObject)m_outChannel).EndOpen, null);
        }
        catch (Exception ex)
        {
            Console.WriteLine(ex);
            var tsk = new TaskCompletionSource<bool>();
            tsk.SetException(ex);
            m_completion = tsk.Task;
        }
    }

    public Task ChannelOpened
    {
        get
        {
            return m_completion;
        }
    }

    public void SendToPeer(string data)
    {
        m_outChannel.SendData(data);
    }

    // IDataChannel method(s), handle incoming traffic
    public void SendData(string data)
    {
        Console.WriteLine("{0} Received data: {1}", m_name, data);
    }

    // cleanup code omitted for brevity
}

class Program
{
    static void Main(string[] args)
    {
        var peer1 = new WCFP2PTransport("WCF_P2P_Test", "Peer1");
        var peer2 = new WCFP2PTransport("WCF_P2P_Test", "Peer2");

        Task.WaitAll(peer1.ChannelOpened, peer2.ChannelOpened);

        peer1.SendToPeer("Packet1");
        peer2.SendToPeer("Packet2");

        Console.ReadKey();
    }
}

Output of this application is

Peer1 Received data: Packet1
Peer2 Received data: Packet1
Peer1 Received data: Packet2
Peer2 Received data: Packet2

while I would like to see

Peer2 Received data: Packet1
Peer1 Received data: Packet2

Of course I can do some tricks in my application to filter out unwanted incoming traffic but before doing that I would like to know

  1. Is it possible to configure WCF to not call the peer back in response to its own call?

  2. What is the best way to restrict peers to only connect in pairs and each peer connecting to specific peer only? I can think of using unique P2P service name for each pair that is why there is an argument providing it to the peer class constructor?

2 Answers2

2

You need the RemoteOnlyMessagePropagationFilter: when you create your Peer 2 Peer Channel:

        var sourceContext = new InstanceContext(sourceObject);

        var sourceFactory = new DuplexChannelFactory<TChannel>(sourceContext, endpointName);

        TChannel sourceProxy = sourceFactory.CreateChannel();

        var remoteOnlyFilter = new RemoteOnlyMessagePropagationFilter();

        var peerNode = ((IClientChannel)sourceProxy).GetProperty<PeerNode>();
        peerNode.MessagePropagationFilter = remoteOnlyFilter;
        try
        {
            sourceProxy.Open();
        }
        catch (Exception)
        {
            sourceProxy.TryCloseOrAbort();
            throw;
        }
1

The most immediate answer I can think of would be to use an identification scheme and make sure you aren't sending to yourself. Once you've identified all the peers you are connected to (a managed list maybe?) just iterate over that and send the packets to each one.

Pseudonym
  • 2,052
  • 2
  • 17
  • 38