UDP Multicast Sending (using JoinMulticastGroup) in C# - c#

I know there are plenty of examples around the web regarding UDP multicasting in C#. This is more to get a clarification on the need to include the method JoinMulticastGroup when sending only. Most code examples I have come across nearly always include this method as part of the initialisation code. But surely if the program or class is only ever sending, then it is not required?
i.e. on another stackoverflow question someone uses the code
public void SendMessage(string message)
{
var data = Encoding.Default.GetBytes(message);
using (var udpClient = new UdpClient(AddressFamily.InterNetwork))
{
var address = IPAddress.Parse("224.100.0.1");
var ipEndPoint = new IPEndPoint(address, 8088);
udpClient.JoinMulticastGroup(address);
udpClient.Send(data, data.Length, ipEndPoint);
udpClient.Close();
}
}
Is the line udpClient.JoinMulticastGroup(address); not actually redundant in this case?

JoinMulticastGroup is indeed for enabling the socket to receive multicast packets destined for that group address. If your client is only sending, then it's not strictly necessary.
However, it doesn't hurt, and does help make the code clear that you're "part of" that multicast group. In this way, if the requirements change in the future, and this application needs to receive packets, then it will already be part of the multicast group.
A source host sends data to a multicast group by simply setting the destination IP address of the datagram to be the multicast group address. Any host can become a source and send data to a multicast group. Sources do not need to register in any way before they can begin sending data to a group, and do not need to be members of the group themselves.
-- metaswitch.com

Related

Can't receive message with Zeromq PGM protocol

I'm trying to create a server/client zeromq based PUB-SUB using PGM protocol, all on my local computer.
For some reason I get stuck on:
string a = clientsocket.Receive(Encoding.Unicode);
It's just for the test and I don't get an exception, the program simply waits.
Server code:
var context = ZmqContext.Create();
ZmqSocket serversocket = context.CreateSocket(SocketType.PUB);
try
{
serversocket.Bind("epgm://192.168.137.127;224.0.0.1:5555");
}
catch (ZmqException)
{
throw;
}
int x = 0;
Console.WriteLine("UP");
while (x < 100)
{
serversocket.Send("hello",Encoding.Unicode);
Console.WriteLine("hello sent {0}",x.ToString());
Thread.Sleep(2000);
x++;
}
Client code:
context = ZmqContext.Create();
clientsocket = context.CreateSocket(SocketType.SUB);
try
{
clientsocket.Connect("epgm://192.168.137.127;224.0.0.1:5555");
}
catch (ZmqException)
{
throw;
}
clientsocket.SubscribeAll();
clientsocket.ReceiveReady += PollingItemEvens;
string a = clientsocket.Receive(Encoding.Unicode);
if (a == "hello")
{
Application.Run(_form1);
}
var poller = new Poller(new List<ZmqSocket> {clientsocket});
while (true)
{
poller.Poll();
}
Edit [2014-08-04 1640 UTC+0000]
i have changed the epgm IP after reading the documentation.
yet it didnt solve the problem...
my IPv4 is 192.168.137.127
its a hotspot seance im on a laptop, it makes any different?
and can i see the epgm on 'netstat' on windwos cmd?
because i dont see anything
You should take a look at the pgm/epgm documentation for 0MQ:
In particular:
Connecting a socket
When connecting a socket to a peer address using zmq_connect() with
the pgm or epgm transport, the endpoint shall be interpreted as an
interface followed by a semicolon, followed by a multicast address,
followed by a colon and a port number.
An interface may be specified by either of the following:
•The interface name as defined by the operating system.
•The primary IPv4 address assigned to the interface, in its numeric representation.
Interface names are not standardised in any way and should be assumed
to be arbitrary and platform dependent. On Win32 platforms no short
interface names exist, thus only the primary IPv4 address may be used
to specify an interface.
A multicast address is specified by an IPv4 multicast address in its
numeric representation.
If you follow the documentation, an address of "epgm://224.0.0.1:8200" is invalid: it is missing the interface part of the address.
Pragmatic General Multicast PGM / EPGM
Uses a bit different structure for Addressing, with interface part added:
/* Connecting to the multicast address 224.0.0.1, port 8200, */
/* using the <localhost> first Ethernet network interface on Linux */
/* and the Encapsulated PGM protocol */
rc = zmq_connect( socket, "epgm://eth0;224.0.0.1:8200" );
assert ( rc == 0 );
/* Connecting to the multicast address 224.0.0.1, port 8200, */
/* using the <localhost> network interface setup with the address 192.168.1.1 */
/* and the standard PGM protocol */
rc = zmq_connect( socket, "pgm://192.168.1.1;224.0.0.1:8200" );
assert ( rc == 0 );
Now check and repair the ISO-OSI-L3 network addresses on the server side so that they match the valid local IPv4 network address, where your server resides and where it attempts to .PUB it's service.
Addendum
The 802.11 (Wi-Fi) standards specify support for multicasting as part of asynchronous services. An 802.11-client station, such as a wireless laptop or PDA (not an access point), begins a multicast delivery by sending multicast packets in 802.11 unicast data frames directed to only the access point. The access point responds with an 802.11 acknowledgement frame sent to the source station if no errors are found in the data frame.
If the 802.11-client sending the frame doesn't receive an acknowledgement, then the client will retransmit the frame. With multicasting, the leg of the data path from the wireless 802.11-client to the access point includes transmission error recovery. The 802.11 protocols ensure reliability between stations in both infrastructure and ad hoc configurations when using unicast data frame transmissions.
After receiving the unicast data frame from the 802.11-client, the access point transmits the data (that the originating 802.11-client wants to multicast) as a multicast frame, which contains a group address as the destination for the intended recipients. Each of the destination stations can receive the frame; however, they do not respond with acknowledgements. As a result, multicasting doesn't ensure a complete, reliable flow of data.
The lack of acknowledgements with multicasting means that some of the data your application is sending may not make it to all of the destinations, and there's no indication of a successful reception.
A note from Martin Sustrik ( co-father of ZeroMQ ):
However, it should be noted that multicast transports are inherently
complex to set up and are often fail due to inadequate networking
hardware, incorrect HW/OS setup etc.
Next step
Would be useful to post both the:
Key-benefits that made you to opt for EPGM transportClass
An application-neutral Validation-test-case for proving the subsequent phases of the life-cycle of each isolated parts of { ZeroMQ-layer | ZeroMQ-primitives } { are | are not } working as you expected them to.
May be inspired by: https://www.mail-archive.com/zeromq-dev#lists.zeromq.org/msg01580.html

Does broadcasting over UDP repeatedly send its packet, or just once?

Using C#, does broadcasting over UDP repeatedly send its packet, or just once?
I've never used this technology before and I want to temporarily broadcast small bit of info (a small one line string) over the LAN. If the receiving end isn't ready will the broadcast repeat itself or was it a one time thing? The code I'm using is from here. So what I want to start the Broadcaster one one machine and a few minutes later start the receiver and retrieve what the broadcaster sent.
Here is the code
using System;
using System.Net;
using System.Net.Sockets;
using System.Text;
class Broadcst
{
public static void Main()
{
Socket sock = new Socket(AddressFamily.InterNetwork, SocketType.Dgram,
ProtocolType.Udp);
IPEndPoint iep1 = new IPEndPoint(IPAddress.Broadcast, 9050);
IPEndPoint iep2 = new IPEndPoint(IPAddress.Parse("192.168.1.255"), 9050);
string hostname = Dns.GetHostName();
byte[] data = Encoding.ASCII.GetBytes(hostname);
sock.SetSocketOption(SocketOptionLevel.Socket,SocketOptionName.Broadcast, 1);
sock.SendTo(data, iep1);
sock.SendTo(data, iep2);
sock.Close();
}
}
UDP by design only sends a packet once. It has no concept of handshakes (unlike TCP), error correction, or transmission guarantees. You can't even be sure that your data gets to where you send it unless you manually request checksums or something like that.
Wikipedia has a nice section on this: Reliability and congestion control solutions in UDP.
So, yes, you will need to implement transmission guarantee code if you want reliability. But what if the message from the recipient saying that the data was received is delayed? Well, then you need to implement some kind of timeout. What if the message gets lost? You need to resend the data to the recipient. How do you know if the recipient gets it this time? Etc...
If you don't want to do this, then I'd suggest looking into TCP which automatically manages this for you.

C# Socket.SendTo in a loop eventually causes SocketException (depends on the router)

I am doing some basic Socket messaging. I have a routine that works well but there is a problem under load.
I'm using UDP to do a connectionless SendTo to basically do a ping-like operation to see if any of my listeners are out there on the LAN. Ideally I would just use the broadcast address, but Wireless routers don't seem to relay my broadcast. My work around is to iterate through all IPs on the Subnet and send my data gram to each IP. The other PCs are listening and if they get the message they will reply and that is how I get Peers to find each other. Here is the code that is in the loop which sends the data gram to each IP in the subnet.
string msgStr = "some message here...";
byte[] sendbuf = Encoding.ASCII.GetBytes(msgStr);
Socket socket = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp);
socket.Blocking = true;
socket.SendTo(sendbuf, remoteEndPt);
//socket.Close();
This works, but when the Subnet range is large, say 255.255.0.0 (meaning ~60,000 IPs to iterate through) I will eventually get a SocketException with error code "10022", meaning "Invalid Argument". This tends to happen after ~10,000 or so successful sends then I start to see this error. Also, the router I use at work handles it and is presumably a high powered router, but the cheap-o one in my lab is the one that produces the error.
If I put in a wait time after catching the SocketException and before resuming the loop it will typically recover but eventually I'll get the error again.
I think what is happening is that the buffer on the router gets full and I cannot send anymore data. The higher quality one at work can handle it but the cheap-o one gets bogged down. Does that sound plausible?
A couple questions:
1) When using SendTo in a connectionless manner, do I need to call Close() on my Socket?
I've haven't seen any benefit in calling Close(), but when I do call Close() it severely slows down my iteration (I have it commented out above because it does slow things down a lot). Does this make sense?
2) Is there a way for me to tell I should wait before trying to send more data? It doesn't seem right to just catch the Exception which I still don't know what the cause of it is.
Thanks, J.
I am not sure that is the router only but I suspect that you are also running into some limit in the OS...
Any reason you are creating the Socket every time you send ?
Just reuse it...
Anyways according to http://msdn.microsoft.com/en-us/library/system.net.sockets.socket.aspx it is a good idea to call Shutdown() and then Close() on the Socket... perhaps not with every send but every 255 IPs or so...
Checkout UdpClient - that could make implementation easier / more robust
EDIT - as per comment:
IF you want a Socket reuse "cache"... this for example would make sure that a specific Socket is only used every 256 checks...
// build/fill your Socket-Queue for example in the con
class SocketExample
{
Queue<Socket> a = new Queue<Socket>();
SocketExample ()
{
int ii = 0, C = 256;
for (ii = 0; ii < C; C++)
{
a.Enqueue (new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp));
}
}
// in your function you just dequeue a Socket and use it,
// after you are finished you enqueue it
void CheckNetIP (some parameters...)
{
Socket S = a.Dequeue();
// do whatever you want to do...
// IF there is no exception
a.Enqueue(S);
}
}

UdpClient receive on broadcast address

In c# I am using the UdpClient.Receive function:
public void StartUdpListener(Object state)
{
try
{
udpServer = new UdpClient(new IPEndPoint(IPAddress.Broadcast, 1234));
}
catch (SocketException ex)
{
MessageBox.Show(ex.ErrorCode.ToString());
}
IPEndPoint remoteEndPoint = null;
receivedNotification=udpServer.Receive(ref remoteEndPoint);
...
However I am getting a socket exception saying that the address is not available with error code 10049
What do I do to negate this exception?
Here's the jist of some code I am currently using in a production app that works (we've got a bit extra in there to handle the case where the client are server apps are running on a standalone installation). It's job is to receive udp notifications that messages are ready for processing. As mentioned by Adam Alexander your only problem is that you need to use IPAddress.Any, instead of IPAddress.Broadcast. You would only use IPAddress.Broadcast when you wanted to Send a broadcast UDP packet.
Set up the udp client
this.broadcastAddress = new IPEndPoint(IPAddress.Any, 1234);
this.udpClient = new UdpClient();
this.udpClient.Client.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReuseAddress, true);
this.udpClient.ExclusiveAddressUse = false; // only if you want to send/receive on same machine.
And to trigger the start of an async receive using a callback.
this.udpClient.Client.Bind(this.broadcastAddress);
this.udpClient.BeginReceive(new AsyncCallback(this.ReceiveCallback), null);
Hopefully this helps, you should be able to adapt it to working synchronously without too much issue. Very similar to what you are doing. If you're still getting the error after this then something else must be using the port that you are trying to listen on.
So, to clarify.
IPAddress.Any = Used to receive. I want to listen for a packet arriving on any IP Address.
IPAddress.Broadcast = Used to send. I want to send a packet to anyone who is listening.
for your purposes I believe you will want to use IPAddress.Any instead of IPAddress.Broadcast. Hope this helps!
That error means the protocol cant bind to the selected IP/port combination.
I havent used UDP broadcast in ages, but I do recall you need to use different IP ranges.
There's nothing wrong with the way you have configured your UdpClient. Have you tried a different port number? Perhaps 1234 is already in use on your system by a different app.

How to do a UDP multicast across the local network in c#?

I am trying to get some simple UDP communication working on my local network.
All i want to do is do a multicast to all machines on the network
Here is my sending code
public void SendMessage(string message)
{
var data = Encoding.Default.GetBytes(message);
using (var udpClient = new UdpClient(AddressFamily.InterNetwork))
{
var address = IPAddress.Parse("224.100.0.1");
var ipEndPoint = new IPEndPoint(address, 8088);
udpClient.JoinMulticastGroup(address);
udpClient.Send(data, data.Length, ipEndPoint);
udpClient.Close();
}
}
and here is my receiving code
public void Start()
{
udpClient = new UdpClient(8088);
udpClient.JoinMulticastGroup(IPAddress.Parse("224.100.0.1"), 50);
receiveThread = new Thread(Receive);
receiveThread.Start();
}
public void Receive()
{
while (true)
{
var ipEndPoint = new IPEndPoint(IPAddress.Any, 0);
var data = udpClient.Receive(ref ipEndPoint);
Message = Encoding.Default.GetString(data);
// Raise the AfterReceive event
if (AfterReceive != null)
{
AfterReceive(this, new EventArgs());
}
}
}
It works perfectly on my local machine but not across the network.
-Does not seem to be the firewall. I disabled it on both machines and it still did not work.
-It works if i do a direct send to the hard coded IP address of the client machine (ie not multicast).
Any help would be appreciated.
Does your local network hardware support IGMP?
It's possible that your switch is multicast aware, but if IGMP is disabled it won't notice if any attached hardware subscribes to a particular multicast group so it wouldn't forward those packets.
To test this, temporarily connect two machines directly together with a cross-over cable. That should (AFAICR) always work.
Also, it should be the server half of the code that has the TTL argument supplied to JoinMulticastGroup(), not the client half.
I've just spent 4 hours on something similar (I think), the solution for me was:
client.Client.Bind(new IPEndPoint(IPAddress.Any, SSDP_PORT));
client.JoinMulticastGroup(SSDP_IP,IP.ExternalIPAddresses.First());
client.MulticastLoopback = true;
Using a specific (first external) IP address on the multicast group.
I can't see a TTL specified anywhere in the code. Remember that TTL was originally meant to be in unit seconds, but is has become unit hops. This means that by using a clever TTL you could eliminate passing through the router. The default TTL on my machine is 32 - I think that should be more than adequate; but yours may actually be different (UdpClient.Ttl) if your system has been through any form of a security lockdown.
I can't recommend the TTL you need - as I personally need to do a lot of experimentation.
If that doesn't work, you could have a look at these articles:
OSIX Article
CodeProject Article
All-in-all it looks like there has been success with using Sockets and not UdpClients.
Your chosen multicast group could also be local-only. Try another one.
Your physical network layer could also be causing issues. I would venture to question switches and direct (x-over) connections. Hubs and all more intelligent should handle them fine. I don't have any literature to back that, however.

Categories

Resources