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());
}
}
}
}
}
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).
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.
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.
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.
You may need to enable loopback mode on the socket.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With