简体   繁体   English

同一进程中两个套接字之间的UDP组播

[英]UDP Multicast between two sockets in the same process

I'm writting some tests for classes that handles UDP multicast communication. 我正在为处理UDP多播通信的类编写一些测试。 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. 我将测试设计为使用环回接口(127.0.0.1)进行测试,因为我不希望它们干扰网络上的其他程序/设备。

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. 在我的“单元测试”中,我有一个经过测试的套接字,该套接字连接了给定的多播组并绑定到127.0.0.1,而发送方套接字也加入了相同的多播组并绑定到127.0.0.1,两者当然都是在同一过程中进行的。

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. 我的错误是认为本地主机上的UDP 可能是可靠的。 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). 下面的测试程序显示,侦听套接字从未接收到第一个UDP数据包(至少在我的计算机上)(但其他进程仍接收到它)。

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? 有谁知道为什么第一个UDP数据包永远不会到达?

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. 这解释了为什么它可以超时。

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

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

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