zeromq / netmq

A 100% native C# implementation of ZeroMQ for .NET
Other
2.97k stars 745 forks source link

tcp://*:5000 vs tcp://localhost:5000 vs tcp://127.0.0.1:5000 vs tcp://<compname>:5000 #121

Closed reiroldan closed 10 years ago

reiroldan commented 10 years ago

As per dnallicus post on the mailing list:

Hi,

I am converting a project from clrzmq to NetMQ and I am running into issues with interface names in my TCP connections. In clrzmq, binding to tcp://:5000 means I can reach that socket using tcp://localhost:5000 or tcp://:5000, where is the dns host name of the computer. In NetMQ, binding to tcp://:5000 seems to only connect to tcp://127.0.0.1:5000, which means I can't even reach it from outside the local computer. If I bind to tcp://localhost:5000, then I can only reach the socket by connecting to tcp://localhost:5000. Likewise, if I use tcp://:5000 I can only connect using tcp://:5000. From the ZMQ docs, it seems that binding to tcp://*:5000 should listen for all interfaces, so it should handle any of localhost, , or 127.0.0.1.

What is the recommended way of doing the bind and connect in NetMQ. Is it to use :5000 for both the bind and the connect?

Thanks

dnallicus commented 10 years ago

I took a brief look at this. It looks like the issue is that each of these binds on a different IP address:

* -> 0.0.0.0

127,0.0.1 -> 127,0.0.1

localhost -> 0.0.0.0.0.1 (IPv6)

compname -> full IPv6 address

There is a parameter that is called IPv4Only that defaults to true, however, this parameter is not used in the function TcpAddress.Resolve function. The relevant line is: ipAddress = Dns.GetHostEntry(addrStr).AddressList.FirstOrDefault();

In the host entry addresslist, the IPv6 addresses are first for my local computer. I changed this line to filter out the IPv6 if ip4only is set:: ipAddress = Dns.GetHostEntry(addrStr).AddressList.FirstOrDefault(s => !ip4Only || s.AddressFamily == AddressFamily.InterNetwork);

Then using * will properly accept all incoming connections from localhost, 127.0.0.1, compname, or the exact IP of my computer. This is a fix for when you are using IPv4 only, but according to the option documentation it should work when you choose IPv6 and accept v4 and v6 connections. I think that is a much harder thing to fix.

    //  If 1, indicates the use of IPv4 sockets only, it will not be
    //  possible to communicate with IPv6-only hosts. If 0, the socket can
    //  connect to and accept connections from both IPv4 and IPv6 hosts.
    public bool IPv4Only { get; set; }
somdoron commented 10 years ago

@reiroldan and @dnallicus can you write a small unit test with what should be the expected result? I'm working on a bug fixes branch and want to solve this...

dnallicus commented 10 years ago

The following unit test should succeed, because 127.0.0.1, localhost, and the should all be valid ways to connect to the local computer. With the code change I mentioned above, this will work as all of them will use ipv4. In the code on master, localhost and the will use ipv6 and thus fail to connect. The Send call below will block indefinitely because it is waiting for a connection to send the message to. I don't think this is correct behavior (I thought it would put the message on an internal queue until outgoing high watermark is met).

    [Test]
    public void BindToLocal()
    {
        var validAliasesForLocalHost = new[] { "127.0.0.1", "localhost", System.Net.Dns.GetHostName() };
        foreach (var alias in validAliasesForLocalHost)
        {
            using (NetMQContext context = NetMQContext.Create())
            {
                using (NetMQSocket localDealer = context.CreateDealerSocket())
                {
                    localDealer.Bind("tcp://*:5002");

                    using (NetMQSocket connectingDealer = context.CreateDealerSocket())
                    {
                        connectingDealer.Connect("tcp://" + alias + ":5002");

                        localDealer.Send("test");

                        Assert.AreEqual("test", connectingDealer.ReceiveString());
                        Console.WriteLine(alias + " connected ");
                    }
                }
            }
        }
    }
}
somdoron commented 10 years ago

thanks @dnallicus, as you suggested ip4only is checked, you can take a look at: https://github.com/somdoron/netmq/commit/29cb8568207aeb85e237138effc2b2abbe1cf1af

will add a pull request soon

somdoron commented 10 years ago

@reiroldan can you confirm it solved now?

reiroldan commented 10 years ago

@somdoron, It was @dnallicus who brought up the issue, I copied it over from the google groups to convert it into an issue :)

somdoron commented 10 years ago

@dnallicus can you confirm?