I am new to multicast programming. So far I can successfully send and receive multicast messages from two separate processes (a sender and a receiver). My problem is with the receiver...
ReceiverCode:
private static void ReceiveMulticastMessages()
{
var groupEndPoint = new IPEndPoint(IPAddress.Parse("238.8.8.8"), 23888);
var localEndPoint = new IPEndPoint(IPAddress.Any, 23888);
using (var udpClient = new UdpClient())
{
udpClient.Client.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReuseAddress, true);
udpClient.Client.Bind(localEndPoint);
udpClient.JoinMulticastGroup(groupEndPoint.Address, localEndPoint.Address);
while (true)
{
var remoteEndPoint = new IPEndPoint(IPAddress.Any, 0);
var bytes = udpClient.Receive(ref remoteEndPoint);
var message = Encoding.ASCII.GetString(bytes);
Console.WriteLine(message);
}
}
}
The above code works as long as I specify port 23888 for the localEndPoint. If I change the local port number, no messages are received. I would prefer to set it to 0 so the OS could choose the port. Why can I not specify a different local port than that of multicast group?
Assuming the local endpoint port must match the multicast group port, how does a client deal with a local port conflict?
On the flip side, how can an application (a multicast sender) choose a multicast group port such that any subscribers will not have a port conflict?
When sending any UDP message (not just muticast messages), the port that the sender sends to must match the port that the receiver is listening on. That's how messages get to the right place. If a message is sent to a port other that the one the receiver is bound to, the receiver won't get it.
So a port number needs to be defined that the receiver(s) will listen on and the server will send to.
Related
I'm working on a UWP/Xamarin.Android app that uses SSDP multicast search to detect other devices on the local network that are running the same app. I've been able to send out multicast messages with UdpClient, but I'm not able to receive multicast messages. It hangs on UdpClient.ReveiveAsync() and never receives a message. Any help in getting this to work would be greatly appreciated.
Here's the coding I'm using to initialize the UdpClient and receive messages:
protected override Task CreateResourcesAsync()
{
address = IPAddress.Parse(Settings.Address ?? "239.255.255.250");
port = Settings.Port ?? 1900;
IPEndPoint localEndPoint = new IPEndPoint(IPAddress.Any, port);
client = new UdpClient() { MulticastLoopback = true };
client.Client.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReuseAddress, true);
// This line was to test setting SO_REUSE_MULTICASTPORT on Windows, it didn't help
//client.Client.SetSocketOption(SocketOptionLevel.Socket, (SocketOptionName)0x3008, true);
client.Client.Bind(localEndPoint);
client.JoinMulticastGroup(address);
Task.Run(ReceiveThread);
return Task.CompletedTask;
}
async Task ReceiveThread()
{
while (IsActive && client != null)
{
// Does not receive any messages so the Task returned from client.ReceiveAsync() does not complete
UdpReceiveResult request = await client.ReceiveAsync();
}
}
I've tried using different multicast addresses and ports other than the SSDP ones, I've also tried binding to both IPAddress.Any and my local IP address, but neither works.
I'm able to receive multicast messages using the WinRT DatagramSocket, but I can't continue to use WinRT as the app needs to run on Android. I'll include my DatagramSocket code just in case it can be used to help somehow:
DatagramSocket socket = new DatagramSocket();
// If MulticastOnly is not true, it will bind to the port but won't receive messages
socket.Control.MulticastOnly = true;
socket.MessageReceived += async (sender, args) =>
{
// Immediately receives SSDP search requests from various devices on my network
};
await socket.BindServiceNameAsync("1900");
socket.JoinMulticastGroup("239.255.255.250");
I'm writing a C# program that needs to send the same data to multiple specific recipients. I can't use multicast because that sends the data to everyone listening on the multicast address.
My current solution is to just iterate through the recipients and send the data to each of them separately but I'm looking for something a little more efficient.
Here's my current solution:
public void SendToMultiple(IPAddress[] Recipients, byte[] Data)
{
UdpClient Client = new UdpClient();
foreach(IPAddress Recipient in Recipients)
{
Client.Send(Data, Data.Length, new IPEndPoint(Recipient, PORT));
}
Client.Close();
}
To the best of my knowledge you can either use Unicast, Multicast or Broadcast.
Since you are only interested in sending to a particular set of clients, I can only recommend Unicast as the other two will send to those also listening.
The only thing I can think of to make it more efficient is to put the code in maybe a Parallel.Foreach loop and create the UdpClient in there and then send the data out?
public void SendToMultiple(IPAddress[] Recipients, byte[] Data)
{
Parallel.ForEach(Recipients,
Recipient =>
{
UdpClient Client = new UdpClient();
Client.Send(Data, Data.Length, new IPEndPoint(Recipient, PORT));
Client.Close();
});
}
I have an embedded Ethernet interface (Lantronix XPort) that responds to a UDP broadcast with its identifying information.
I am able to multicast the "magic packet" and datagrams are received by the listener correctly, however I also need to find out what IP Address send that response datagram. If it were TCP, I would do socket.RemoteEndPoint, but that throws an exception when applied to a UDP socket.
public class Program
{
public static void Main(string[] args)
{
// magic packet
byte[] magicPacket = new byte[4] { 0, 0, 0, 0xf6 };
// set up listener for response
Socket sendSocket = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp);
// EDIT: Also, had to add this to for it to broadcast correctly
sendSocket.EnableBroadcast = true;
IPEndPoint listen_ep = new IPEndPoint(IPAddress.Any, 0);
sendSocket.Bind(listen_ep);
// set up broadcast message
EndPoint send_ep = new IPEndPoint(IPAddress.Parse("192.168.255.255"), 30718);
sendSocket.SendTo(magicPacket, magicPacket.Length, SocketFlags.None, send_ep);
DateTime dtStart = DateTime.Now;
while (true)
{
if (sendSocket.Available > 0)
{
byte[] data = new byte[2048];
// throws SocketException
//IPEndPoint ip = sendSocket.RemoteEndPoint as IPEndPoint;
sendSocket.Receive(data, SocketFlags.None);
if (data.Length > 4)
{
PrintDevice(data);
}
}
if (DateTime.Now > dtStart.AddSeconds(5))
{
break;
}
Console.WriteLine(".");
Thread.Sleep(100);
}
// wait for keypress to quit
Console.WriteLine("Press any key to exit.");
Console.ReadKey();
}
}
Any thoughts? Is there a better strategy to reading the response datagrams that would let me ascertain the Remote IP Address?
EDIT:
As is typical, the minute I post on SO, a moment of clarity hits me.
Turns out I can just do this:
EndPoint remote_ep = new IPEndPoint(IPAddress.Any, 0);
// Use ReceiveFrom instead of Receieve
//sendSocket.Receive(data, SocketFlags.None);
sendSocket.ReceiveFrom(data, ref remote_ep);
And remote_ep now contains the remote endpoint information!
Take a look at ReceiveFrom instead of Receive, it will let you pass in a reference to an Endpoint.
What about Asynchronous socket?I didn't find any way to get the remote IP address. (Asynchronous method ReceiveFromAsync is my only option in wp8)
EndPoint remote_ep = new IPEndPoint(IPAddress.Any, 0); // Use ReceiveFrom instead of
sendSocket.Receive(data, SocketFlags.None);
sendSocket.ReceiveFrom(data, ref remote_ep);
i think it works for IP but it fails for port number
if you would notice
try chaging 0 to something else like 6530 4expl
it system will would generate it's random port number
Any ideas to why is it ?
P.S. Any ideas how can i change my user name here .... ?
FOUND IT : the abstract class only needed for representation of port it is in value not out
since there's no bind done before hand this operation ref EndPoint needed to represent the sender. Meaning that it is there to show senders port and IP not to specify from where to get the communication. And EndPoint instantiation is really just a formality seems like it is overriden by system with senders address anyway. I think it has to do somethign with the way UDP protocol works.
But all in all the ref EndPoint is there only shows where u got the packet from and only it does not specify where u want u'r commuicatino to be from.
I'm trying to send out a multicast packet then receive the response that comes back (there should only be one response.) I can send the packet, and wireshark shows the reply but my program never receives it. I've tried a few different things but this is the current state of my non-functioning code.
IPEndPoint epReceive = new IPEndPoint(IPAddress.Any, 5355);
IPEndPoint epSend = new IPEndPoint(IPAddress.Parse("224.0.0.252"), 5355);
UdpClient sendClient = new UdpClient();
sendClient.ExclusiveAddressUse = false;
sendClient.Client.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReuseAddress, true);
sendClient.Client.Bind(epReceive);
sendClient.Send(packet, packetLength, epSend);
byte[] buffer = sendClient.Receive(ref epReceive);
sendClient.Close();
This code just hangs on the sendClient.Receive() line. I realise this code is currently blocking and could/should be threaded, but for the purposes it's being used for it's not a concern.
I am writing a couple of functions to allow me to send out a UDP broadcast / multicast using both IPv4 and IPv6. The follow code does this. The problem I have is that it only does this for a single adapter. If I have two network adapters fitted to my PC it doesn’t send the broadcast out on both. Is it possible to have a single socket configured to handle both IPv4 and IPv6 and to send and receive on all NICs? Or do I have to create separate sockets for each IP address?
public void CreateBroadcaster(CancellationToken cancellationToken, int discoveryPort, int advancePort)
{
_cancellationToken = cancellationToken;
_broadcastEndpoint = new IPEndPoint(IPAddress.Broadcast, advancePort);
var epLocal = new IPEndPoint(IPAddress.IPv6Any, discoveryPort);
_broadcaster = new UdpClient(epLocal);
var soc = new Socket(AddressFamily.InterNetworkV6, SocketType.Dgram, ProtocolType.Udp);
soc.SetSocketOption(SocketOptionLevel.IPv6, SocketOptionName.IPv6Only, false);
_broadcaster.Client = soc;
_broadcaster.Client.DualMode = true;
_broadcaster.Client.Bind(epLocal);
_broadcaster.EnableBroadcast = true;
_broadcaster.MulticastLoopback = true;
_broadcaster.Client.SetSocketOption(
SocketOptionLevel.IPv6,
SocketOptionName.AddMembership,
new IPv6MulticastOption(IPAddress.Parse("ff02::1")));
}
public void SendPingsOnAdapterLocalSubnets()
{
_broadcaster.Send(_sendData, _sendData.Length, _broadcastEndpoint);
}
You need 2 separate sockets. On the low level, the sockaddr struct will have one or other. IPv4(AF_INET) or IPv6(AF_INET6).
http://www.gnu.org/software/libc/manual/html_node/Address-Formats.html#Address-Formats