Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

UDP Multicast between two sockets in the same process

I'm writting some tests for classes that handles UDP multicast communication. I designed the tests to use the loopback interface (127.0.0.1) for the tests because I don't want the them to interfer with other programs/devices on the network.

In my "unit test" I have a tested socket that joins a given multicast group and binds to 127.0.0.1 and a sender socket that also joined the same multicast group and binds to 127.0.0.1, both of course in the same process.

To be sure that the message is sent I have another test program (so another process) that also joins the multicast group and outputs everything that is sent to it.

The problem is that my tested socket never receive what the sender sent BUT the test program (so another process) receives it.

Are there some limitation with the combination multiple sockets/multicast/localhost?

New information:

My mistake was to consider that UDP on localhost might be reliable. The below test program shows that the first UDP packet is never received (at least on my computer) by my listening socket (but the other process still receives it).

In my unit tests I am sending one packet and expects specific answers: I cannot afford sending the message two times and receiving the answer only once.

It seems to work reliably if I wait for the first receive timeout to occur before sending the first packet.

Does anyone have an idea why the first UDP packet never arrives?

Here's the code I used in my trials:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Net;
using System.Net.Sockets;
using System.Threading;
using NUnit.Framework;

namespace MulticastTest
{
    [TestFixture]
    public class Program
    {
        static void Main(string[] args)
        {
            new Program().Run();
            Console.WriteLine("Press any key to exit...");
            Console.ReadKey();
        }

    [Test]
    public void Run()
    {
        _waitFirstReadTiemout = new AutoResetEvent(false);
        IPAddress lMulticastAddress = new IPAddress(0xFAFFFFEF);
        IPEndPoint lRemoteEndPoint = new IPEndPoint(lMulticastAddress, 1900);

        // Create sender socket
        Socket lSendSocket = new Socket(AddressFamily.InterNetwork,
                             SocketType.Dgram,
                             ProtocolType.Udp);

        // Allow to share the port 1900 with other applications
        lSendSocket.SetSocketOption(SocketOptionLevel.Socket,
                                SocketOptionName.ReuseAddress,
                                true);

        // Set TTL for multicast packets: socket needs to be bounded to do this
        lSendSocket.SetSocketOption(SocketOptionLevel.IP,
                                SocketOptionName.MulticastTimeToLive,
                                2);

        // Bind the socket to the local end point: this MUST be done before joining the multicast group
        lSendSocket.Bind(new IPEndPoint(IPAddress.Loopback, 55236));

        // Join the multicast group
        lSendSocket.SetSocketOption(SocketOptionLevel.IP,
                        SocketOptionName.MulticastLoopback,
                        true);

        lSendSocket.SetSocketOption(SocketOptionLevel.IP,
                                SocketOptionName.AddMembership,
                                new MulticastOption(lMulticastAddress));

        // Create receiver and start its thread
        Thread lReceiveThread = new Thread(ReceiveThread);
        lReceiveThread.Start();

        int i = 0;
        while (!fStop)
        {
            if (i == 0)
                _waitFirstReadTiemout.WaitOne(10000);

            byte[] lToSend = Encoding.ASCII.GetBytes(DateTime.Now.ToString("yyyyMMdd HHmmss"));
            lSendSocket.SendTo(lToSend, lRemoteEndPoint);
            Console.WriteLine("Sent #" + (i + 1) + ": " + DateTime.Now.ToString("yyyyMMdd HHmmss"));
            Thread.Sleep(1000);
            try
            {
                if (Console.KeyAvailable || i >= 10)
                    fStop = true;
            }
            catch (InvalidOperationException)
            {
                fStop = i >= 10;
            }
            finally
            {
                ++i;
            }
        }
    }

    private AutoResetEvent _waitFirstReadTiemout;

    private bool fStop;

    private void ReceiveThread()
    {
        Socket lSocket = new Socket(AddressFamily.InterNetwork, 
                                    SocketType.Dgram, 
                                    ProtocolType.Udp);

        // Allow to share the port 1900 with other applications
        lSocket.SetSocketOption(SocketOptionLevel.Socket,
                                SocketOptionName.ReuseAddress,
                                true);

        // TTL not required here: we will only LISTEN on the multicast socket
        // Bind the socket to the local end point: this MUST be done before joining the multicast group
        lSocket.Bind(new IPEndPoint(IPAddress.Loopback, 1900));

        // Join the multicast group

        // If the local IP is a loopback one, enable multicast loopback
        lSocket.SetSocketOption(SocketOptionLevel.IP,
                    SocketOptionName.MulticastLoopback,
                    true);

        lSocket.SetSocketOption(SocketOptionLevel.IP,
                                SocketOptionName.AddMembership,
                                new MulticastOption(
                                        new IPAddress(0xFAFFFFEF)));

        lSocket.ReceiveTimeout = 1000;

        byte[] lBuffer = new byte[65000];
        int i = 0;
        while (!fStop)
        {
            try
            {
                int lReceived = lSocket.Receive(lBuffer);
                ++i;
                Console.WriteLine("Received #" + i + ": " + Encoding.ASCII.GetString(lBuffer, 0, lReceived));
            }
            catch (SocketException se)
            {
                _waitFirstReadTiemout.Set();
                Console.WriteLine(se.ToString());
            }
        }
    }
}

}

like image 855
Bruno Knittel Avatar asked Aug 03 '12 10:08

Bruno Knittel


People also ask

Does UDP support multicasting?

The nodes in the network take care of replicating the packet to reach multiple receivers only when necessary. The most common transport layer protocol to use multicast addressing is User Datagram Protocol (UDP).

How does multicast UDP work?

Note: multicast uses UDP and are sent through switches and hubs. To receive a multicast message a host must be configured to receive on that multicast address. All hosts that are configured to receive packets on a particular address are part of a multicast group.

Can a UDP socket be established with a source multicast IP?

Multicasting can only be enabled on UDP sockets. This is because UDP is a connectionless protocol. TCP on the other hand establishes a connection with a specific host.


2 Answers

This most likely is a race between your sending and receiving threads - you send the first packet before the receiver joins the group. This explains why it works with a timeout.

like image 71
Nikolai Fetissov Avatar answered Oct 06 '22 00:10

Nikolai Fetissov


You may need to enable loopback mode on the socket.

like image 36
user207421 Avatar answered Oct 06 '22 00:10

user207421