简体   繁体   中英

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

}

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.

您可能需要在套接字上启用环回模式。

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM