Receiving UDP Multicast Messages on a Multihomed Windows 10 Machine - c#

I have a C# .NET client which listens for UDP multicast messages. I need to receive messages on a single network interface. Sometimes I will not see the messages being received. When I disable the other interfaces, it works.
I attempted to set socket options to a specific interface using code from similar questions on this site, however, I'm unsure if this only affects sending multicast messages and not receiving them?
After my research, I see that the routing table causes this behavior and one solution is to change the routing table, but I prefer not to go that route.
Is it better to join the multicast group on all the interfaces? How would I do that using a UdpClient.
This is the code used to setup my UdpClient:
Getting the interfaces:
public static IEnumerable<NetworkInterface> GetAvailableMulticastInterfaces()
{
NetworkInterface[] nics = NetworkInterface.GetAllNetworkInterfaces();
List<NetworkInterface> availableInterfaces = new List<NetworkInterface>();
foreach (NetworkInterface adapter in nics)
{
IPInterfaceProperties ip_properties = adapter.GetIPProperties();
if (!adapter.GetIPProperties().MulticastAddresses.Any())
continue; // most of VPN adapters will be skipped
if (!adapter.SupportsMulticast)
continue; // multicast is meaningless for this type of connection
if (OperationalStatus.Up != adapter.OperationalStatus)
continue; // this adapter is off or not connected
IPv4InterfaceProperties p = adapter.GetIPProperties().GetIPv4Properties();
if (null == p)
continue; // IPv4 is not configured on this adapter
availableInterfaces.Add(adapter);
}
return availableInterfaces;
}
Setting the Interface:
NetworkInterface networkInterface = Common.Utilities.Network.GetAvailableMulticastInterfaces().Where(nic => nic.Id == attributes.SelectedNetworkInterfaceId).FirstOrDefault();
Getting the Interface Index:
networkInterfaceIndex = (int)IPAddress.HostToNetworkOrder(networkInterface.GetIPProperties().GetIPv4Properties().Index);
Binding the UdpClient:
public void Listen(string multicastGroupAddress, int port, int? networkInterfaceIndex = null)
{
IpAddress = multicastGroupAddress;
Port = port;
_multicastEndPoint = new IPEndPoint(IPAddress.Parse(multicastGroupAddress), port);
Listener = new UdpClient();
Listener.Client.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReuseAddress, true);
if (networkInterfaceIndex != null)
Listener.Client.SetSocketOption(SocketOptionLevel.IP, SocketOptionName.MulticastInterface, networkInterfaceIndex.Value);
var localEndpoint = new IPEndPoint(IPAddress.Any, port);
Listener.Client.Bind(localEndpoint);
Listener.JoinMulticastGroup(IPAddress.Parse(multicastGroupAddress));
while (true)
Process();
}

Ok, so I came back to this and found the right operation to use. One must join on multiple interfaces using
var availableMulticastNics = Utils.GetAvailableMulticastInterfaces();
var listener = new UdpClient();
listener.Client.Bind(new IPEndPoint(IPAddress.Any, endpoint.Port));
foreach (var ni in availableMulticastNics)
{
listener.Client.SetSocketOption(SocketOptionLevel.IP, SocketOptionName.AddMembership, new MulticastOption(multicastEndPoint.Address, ni.GetIPProperties().GetIPv4Properties().Index));
}
// Ready to read socket
Instead of listener.JoinMulticastGroup(ni.GetIPProperties().GetIPv4Properties().Index, multicastEndPoint.Address). Otherwise, I get a SocketException that tells me invalid argument, which someone can shed some light on if they can.

Related

How to properly change a static IP address using the Microframework?

I have my Netduino Plus 2 go out to a web service to look up some values that I would like it to use in my project. One of the values that I have the Netduino check is its preferred IP address. If the Netduino has a different IPAddress than its preferred, I want to change it.
I have a method in my project called BindIPAddress (below) that takes a string.
I am getting a SocketException with a code of 10022 for invalid argument. This happens when I call this.Socket.Bind. My class has a property called Socket to hold the Socket value. Is it because my socket already has an endpoint ? I tried adding this.Socket = null and then this.Socket = new (....... thinking we need a new socket to work with, but this returned the same error.
Please advise how I can change my IP address from one static IP address to another.
public void BindIPAddress(string strIPAddress)
{
try
{
Microsoft.SPOT.Net.NetworkInformation.NetworkInterface.GetAllNetworkInterfaces()[0].EnableStaticIP(strIPAddress, "255.255.240.0", "10.0.0.1");
Microsoft.SPOT.Net.NetworkInformation.NetworkInterface.GetAllNetworkInterfaces()[0].EnableStaticDns(new string[] { "10.0.0.2", "10.0.0.3" });
IPEndPoint ep = new IPEndPoint(IPAddress.Parse(strIPAddress), 80);
this.Socket.Bind(ep);
this.IpAddress = strIPAddress;
}
catch(SocketException exc)
{
Debug.Print(exc.Message);
Debug.Print(exc.ErrorCode.ToString());
}
catch(Exception ex)
{
Debug.Print(ex.Message);
}
//Debug.Print(ep.Address.ToString());
}
There may be 2 possible solutions to this problem. The first one is, you can programatically set the preffered IP addresses as the way you have tried to do so and the second one is, you can use MFDeploy tool, which comes with .NET Micro Framework SDK bundle that allows you to set your embedded device network configuration statically before running your application on it.
1) Since you have not provided rest of the code, here's a proper way to bind your socket to an EndPoint (actually, I would not design that class and binding function as the way you posted here, but just wanted to underline the missing parts of your code):
public void BindIPAddress(string strIPAddr)
{
Socket sock = null;
IPEndPoint ipe = null;
NetworkInterface[] ni = null;
try
{
ipe = new IPEndPoint(IPAddress.Parse(strIPAddr), 80);
sock = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); // Assuming the WebService is connection oriented (TCP)
// sock = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp); // if it broadcasts UDP packets, use this line (UDP)
ni = NetworkInterface.GetAllNetworkInterfaces();
if (ni != null && ni.Length > 0)
{
ni[0].EnableStaticIP(strIPAddr, "255.255.240.0", "10.0.0.1");
ni[0].EnableStaticDns(new string[2] { "10.0.0.2", "10.0.0.3" });
sock.Bind(ipe);
this.Socket = sock;
this.IpAddress = strIPAddr;
}
else
throw new Exception("Network interface could not be retrieved successfully!");
}
catch(Exception ex)
{
Debug.Print(ex.Message);
}
}
2) Or without programming, just by using MFDeploy tool you can set the preferred IP addresses after plugging your embedded device into your PC and following the path below:
MFDeploy > Target > Configuration > Network
then enter the preferred IP addresses. That's simply all.

How to do Network discovery using UDP broadcast

I want to to do network discovery using UDP Broadcast in C#. I don't know how to do this. Can you give me advice on how to do it?
I want to do like this tutorial.
It's very simple to make same thing in C#
Server:
var Server = new UdpClient(8888);
var ResponseData = Encoding.ASCII.GetBytes("SomeResponseData");
while (true)
{
var ClientEp = new IPEndPoint(IPAddress.Any, 0);
var ClientRequestData = Server.Receive(ref ClientEp);
var ClientRequest = Encoding.ASCII.GetString(ClientRequestData);
Console.WriteLine("Recived {0} from {1}, sending response", ClientRequest, ClientEp.Address.ToString());
Server.Send(ResponseData, ResponseData.Length, ClientEp);
}
Client:
var Client = new UdpClient();
var RequestData = Encoding.ASCII.GetBytes("SomeRequestData");
var ServerEp = new IPEndPoint(IPAddress.Any, 0);
Client.EnableBroadcast = true;
Client.Send(RequestData, RequestData.Length, new IPEndPoint(IPAddress.Broadcast, 8888));
var ServerResponseData = Client.Receive(ref ServerEp);
var ServerResponse = Encoding.ASCII.GetString(ServerResponseData);
Console.WriteLine("Recived {0} from {1}", ServerResponse, ServerEp.Address.ToString());
Client.Close();
Here is a different solution that is serverless. I had a need to have a bunch of raspberry pis be aware of each other on a network, but had no guarantees of who would be active. So this approach allows everyone to be a client! The complete library is available on GitHub (disclaimer: I created) and that makes this whole process really reaaaally easy for UWP apps.
https://github.com/mattwood2855/WindowsIotDiscovery
This solution assumes that device names are unique and that you want to use JSON strings as the communication protocol, but you could easily just send any other format. Also, in practice try-catch everything ;)
The general mechanism:
Discover your IpAdress
public string IpAddress
{
get
{
var hosts = NetworkInformation.GetHostNames();
foreach (var host in hosts)
{
if (host.Type == HostNameType.Ipv4) return host.DisplayName;
}
return "";
}
}
Set up your listener
var udpPort = "1234";
var socket = new DatagramSocket();
socket.MessageReceived += ReceivedDiscoveryMessage;
await socket.BindServiceNameAsync(udpPort);`
Handle incoming data
async void ReceivedDiscoveryMessage(DatagramSocket socket, DatagramSocketMessageReceivedEventArgs args)
{
// Get the data from the packet
var result = args.GetDataStream();
var resultStream = result.AsStreamForRead();
using (var reader = new StreamReader(resultStream))
{
// Load the raw data into a response object
var potentialRequestString = await reader.ReadToEndAsync();
// Ignore messages from yourself
if (args.RemoteAddress.DisplayName == IpAddress) return;
// Get the message
JObject jRequest = JObject.Parse(potentialRequestString);
// Do stuff with the data
}
}
Send a message
public async void SendDataMessage(string discoveryMessage)
{
// Get an output stream to all IPs on the given port
using (var stream = await socket.GetOutputStreamAsync(new HostName("255.255.255.255"), udpPort))
{
// Get a data writing stream
using (var writer = new DataWriter(stream))
{
// Write the string to the stream
writer.WriteString(discoveryMessage);
// Commit
await writer.StoreAsync();
}
}
}
The idea would be to send a discovery message containing your ip address and name. Then in the receive message function add the ip-name pairs to a List of devices. Add a little logic to avoid duplicates and update Ip address if the ip changes for a given name.
As a bonus, you can have each device send the list of devices they know about. This allows you to minimize udp traffic by not responding when the sender is aware of you. You can even have the receiver compare the list against their own list to discover other devices.
Redundancy is your friend with UDP, there is no guarantee that a packet will be delivered.
I know it's old but someone may still need this...The accepted answer is great but with this little tweak on the server side it's even better.
Fix for the Ilya Suzdalnitski comment (locks up on the second Client.Receive call):
var responseData = Encoding.ASCII.GetBytes("someData");
while (true)
{
var server = new UdpClient(8888);
var clientEp = new IPEndPoint(IPAddress.Any, 0);
var clientRequestData = server.Receive(ref clientEp);
var clientRequest = Encoding.ASCII.GetString(clientRequestData);
Console.WriteLine($"Recived {clientRequest} from {clientEp.Address}, sending
response: {responseData}");
server.Send(responseData, responseData.Length, clientEp);
server.Close();
}
Because after each response the server is closed and recreated, it can work endlessly without locking.
I had the same question but it was not that easy for me as the answer that #rufanov suggests.
Here some situation I had:
Since my application is running normally in a computer that has several network interfaces, I had the problem that the broadcast message was sent only in one of the adapters. To solve this situation I had to get first all the network adapter list and go one by one sending the broadcast message and receiving the answer message.
It is important that you bind the correct localIpEndPoint to your adapters ip address, otherwise you will have problems with the broadcast address by sending.
After some reserch and work I got to this solution. This code corresponds to the server side and will make the network discovery of all devices answering to the braodcast message.
public static void SNCT_SendBroadcast(out List<MyDevice> DevicesList)
{
DevicesList = new List<MyDevice>();
byte[] data = new byte[2]; //broadcast data
data[0] = 0x0A;
data[1] = 0x60;
IPEndPoint ip = new IPEndPoint(IPAddress.Broadcast, 45000); //braodcast IP address, and corresponding port
NetworkInterface[] nics = System.Net.NetworkInformation.NetworkInterface.GetAllNetworkInterfaces(); //get all network interfaces of the computer
foreach (NetworkInterface adapter in nics)
{
// Only select interfaces that are Ethernet type and support IPv4 (important to minimize waiting time)
if (adapter.NetworkInterfaceType != NetworkInterfaceType.Ethernet) { continue; }
if (adapter.Supports(NetworkInterfaceComponent.IPv4) == false) { continue; }
try
{
IPInterfaceProperties adapterProperties = adapter.GetIPProperties();
foreach (var ua in adapterProperties.UnicastAddresses)
{
if (ua.Address.AddressFamily == System.Net.Sockets.AddressFamily.InterNetwork)
{
//SEND BROADCAST IN THE ADAPTER
//1) Set the socket as UDP Client
Socket bcSocket = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp); //broadcast socket
//2) Set socker options
bcSocket.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.Broadcast, 1);
bcSocket.ReceiveTimeout = 200; //receive timout 200ms
//3) Bind to the current selected adapter
IPEndPoint myLocalEndPoint = new IPEndPoint(ua.Address, 45000);
bcSocket.Bind(myLocalEndPoint);
//4) Send the broadcast data
bcSocket.SendTo(data, ip);
//RECEIVE BROADCAST IN THE ADAPTER
int BUFFER_SIZE_ANSWER = 1024;
byte[] bufferAnswer = new byte[BUFFER_SIZE_ANSWER];
do
{
try
{
bcSocket.Receive(bufferAnswer);
DevicesList.Add(GetMyDevice(bufferAnswer)); //Corresponding functions to get the devices information. Depends on the application.
}
catch { break; }
} while (bcSocket.ReceiveTimeout != 0); //fixed receive timeout for each adapter that supports our broadcast
bcSocket.Close();
}
}
}
catch { }
}
return;
}
For working example, see that project:https://github.com/xmegz/MndpTray
The server periodically sends broadcast messages. The client side receive and process them. Many host information (Os version, IP address, Network interface, etc..) send trought.
udp broadcast cdp lldp

WCF web service discovery on network interfaces with multiple IP addresses

I'm trying to do a webservice discovery using WCF's DiscoveryClient using this code:
// Setup the discovery client (WSDiscovery April 2005)
DiscoveryEndpoint discoveryEndpoint = new UdpDiscoveryEndpoint(DiscoveryVersion.WSDiscoveryApril2005);
DiscoveryClient discoveryClient = new DiscoveryClient(discoveryEndpoint);
// Setup the wanted device criteria
FindCriteria criteria = new FindCriteria();
criteria.ScopeMatchBy = new Uri("http://schemas.xmlsoap.org/ws/2005/04/discovery/rfc3986");
criteria.Scopes.Add(new Uri("onvif://www.onvif.org/"));
// Go find!
criteria.Duration = TimeSpan.FromMilliseconds(duration);
discoveryClient.FindAsync(criteria, this);
This works very well on a machine with a single IP address (10.1.4.25) assigned to the single network interface. The broadcast is sent from 10.1.4.25 to 239.255.255.250, and I get responses from 5 devices all on the same subnet.
However, when the machine has multiple IPs on the same interface, it seems to pick a single source IP and sends the request from that.
In this case, I get a reply from a single device giving a 169.254 address.
I have tried setting UdpDiscoveryEndpoint.TransportSettings.MulticastInterfaceId to a suitable interface ID which hasn't helped as it identifies a single interface, not a specific IP.
The UdpDiscoveryEndpoint.ListenUri property also returns the multicast address, and so won't effect the source IP.
UdpDiscoveryEndpoint.Address is the URN for the discovery protocol.
Is there any way I can force it to send from a specific IP address, or ideally, multiple requests on each configured IP?
I have also tried ONVIF Device Manager that seems to have the same problem.
Note that this is not about making a service bind to a specific, or "all address" IP. It is about the IP a discovery request is sent from.
Well, I had the same problem and after some days of research, reading ONVIF documents and learning some tips about multicasting, I developed this code which works fine.
As an example the main IP address on my network adapter is 192.168.80.55 and I also set another IP(192.168.0.10) in advanced settings. With use of this code I can discover device service of a camera with the IP address of 192.168.0.12.
The most important part of this sample is "DeepDiscovery" method which contains the main idea of iteration on network addresses and multicasting proper Probe message.
I recommend deserialization of the response in "GetSocketResponse" method. Currently, just I extract the service URI using Regex.
As mentioned in this article (https://msdn.microsoft.com/en-us/library/dd456791(v=vs.110).aspx):
For WCF Discovery to work correctly, all NICs (Network Interface
Controller) should only have 1 IP address.
I am doing the exact action that WS-Discovery does and using the standard 3702 port, but I myself build the SOAP envelope
and use Socket class for sending the packet for all IP addresses that have been set for the network interface controller.
class Program
{
static readonly List<string> addressList = new List<string>();
static readonly IPAddress multicastAddress = IPAddress.Parse("239.255.255.250");
const int multicastPort = 3702;
const int unicastPort = 0;
static void Main(string[] args)
{
DeepDiscovery();
Console.ReadKey();
}
public static void DeepDiscovery()
{
string probeMessageTemplate = #"<s:Envelope xmlns:s=""http://www.w3.org/2003/05/soap-envelope"" xmlns:a=""http://schemas.xmlsoap.org/ws/2004/08/addressing""><s:Header><a:Action s:mustUnderstand=""1"">http://schemas.xmlsoap.org/ws/2005/04/discovery/Probe</a:Action><a:MessageID>urn:uuid:{messageId}</a:MessageID><a:ReplyTo><a:Address>http://schemas.xmlsoap.org/ws/2004/08/addressing/role/anonymous</a:Address></a:ReplyTo><a:To s:mustUnderstand=""1"">urn:schemas-xmlsoap-org:ws:2005:04:discovery</a:To></s:Header><s:Body><Probe xmlns=""http://schemas.xmlsoap.org/ws/2005/04/discovery""><d:Types xmlns:d=""http://schemas.xmlsoap.org/ws/2005/04/discovery"" xmlns:dp0=""http://www.onvif.org/ver10/device/wsdl"">dp0:Device</d:Types></Probe></s:Body></s:Envelope>";
foreach (IPAddress localIp in
Dns.GetHostAddresses(Dns.GetHostName()).Where(i => i.AddressFamily == AddressFamily.InterNetwork))
{
var socket = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp);
socket.Bind(new IPEndPoint(localIp, unicastPort));
socket.SetSocketOption(SocketOptionLevel.IP, SocketOptionName.AddMembership, new MulticastOption(multicastAddress, localIp));
socket.SetSocketOption(SocketOptionLevel.IP, SocketOptionName.MulticastTimeToLive, 255);
socket.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReuseAddress, true);
socket.MulticastLoopback = true;
var thread = new Thread(() => GetSocketResponse(socket));
var probeMessage = probeMessageTemplate.Replace("{messageId}", Guid.NewGuid().ToString());
var message = Encoding.UTF8.GetBytes(probeMessage);
socket.SendTo(message, 0, message.Length, SocketFlags.None, new IPEndPoint(multicastAddress, multicastPort));
thread.Start();
}
}
public static void GetSocketResponse(Socket socket)
{
try
{
while (true)
{
var response = new byte[3000];
EndPoint ep = socket.LocalEndPoint;
socket.ReceiveFrom(response, ref ep);
var str = Encoding.UTF8.GetString(response);
var matches = Regex.Matches(str, #"http://\b\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}\b/onvif/device_service");
foreach (var match in matches)
{
var value = match.ToString();
if (!addressList.Contains(value))
{
Console.WriteLine(value);
addressList.Add(value);
}
}
//...
}
}
catch (Exception ex)
{
Console.WriteLine(ex);
//...
}
}
}

UDP: Read data from all network interfaces

I've the following code to read multicast message coming from the network, for a specified IP+Port
private static void ReceiveMessages(int port, string ip, CancellationToken token)
{
Task.Factory.StartNew(() =>
{
using (var mUdpClientReceiver = new UdpClient())
{
var mReceivingEndPoint = new IPEndPoint(IPAddress.Any, port);
mUdpClientReceiver.ExclusiveAddressUse = false;
mUdpClientReceiver.Client.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReuseAddress, true);
mUdpClientReceiver.ExclusiveAddressUse = false;
mUdpClientReceiver.Client.Bind(mReceivingEndPoint);
mUdpClientReceiver.JoinMulticastGroup(IPAddress.Parse(ip), 255);
while (!token.IsCancellationRequested)
{
byte[] receive = mUdpClientReceiver.Receive(ref mReceivingEndPoint);
Console.WriteLine("Message received from {0} ",mReceivingEndPoint);
}
}
});
}
I've two network adapter from which I've data coming on this multicast ip+port(confirmed by two instances of wireshark monitoring each network adapter). I see on wireshark a lot of traffic coming on those port+Ip) for both network cards.
The problem is that on my console, I only see messages coming from one network card.
I double checked with netstat, I don't have any other software listening on my port:
So why am I getting traffic from only one of my two network cards?
EDIT:
I even tried the following:
private static void ReceiveMessages(int port, string ip, CancellationToken token, IEnumerable<IPAddress> ipAddresses)
{
foreach (IPAddress ipAddress in ipAddresses)
{
IPAddress ipToUse = ipAddress;
Task.Factory.StartNew(() =>
{
using (var mUdpClientReceiver = new UdpClient())
{
var mReceivingEndPoint = new IPEndPoint(ipToUse, port);
mUdpClientReceiver.ExclusiveAddressUse = false;
mUdpClientReceiver.Client.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReuseAddress, true);
mUdpClientReceiver.Client.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.Broadcast, 1);
mUdpClientReceiver.Client.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.DontRoute, 1);
mUdpClientReceiver.ExclusiveAddressUse = false;
mUdpClientReceiver.Client.Bind(mReceivingEndPoint);
mUdpClientReceiver.JoinMulticastGroup(IPAddress.Parse(ip), 255);
Console.WriteLine("Starting to listen on "+ipToUse);
while (!token.IsCancellationRequested)
{
byte[] receive = mUdpClientReceiver.Receive(ref mReceivingEndPoint);
Console.WriteLine("Message received from {0} on {1}", mReceivingEndPoint,ipToUse);
}
}
});
}
}
I see the "Starting to listen on theCorrectIP" twice(for my two IPs), but it still display only data coming from one network card.
EDIT 2
I did notice something else that is strange too. If I disable the interface on which I receive all data, and then start the software, I now get data from the other interface. If I activate again the interface and restart the software, I still get the traffic on the non-deactivated card.
And I know for sure that I've devices that respond to me, that are connected only to one network(not both)
EDIT 3
Another thing: if I send a message from me(localhost), on all network card that I've, I see them coming on my two network interfaces. BUT, if I start my program twice, only the first programm get messages, not the second one.
Edit 4
Additional info, following the first comment:
I've two ethernet cards, one with the 10.10.24.78 ip, the other with the 10.9.10.234 ip.
It's not me that send data, but network pieces(the port 5353 with this ip is a know multicast address used for mDNS, so I should receive traffic from things like printer, itunes, macs, and some other pieces of software we created). Data are multicasted on the ip
224.0.0.251 and port 5353.
Here is a code that you could use to send data on severals IPs, but like I described, if you start it in local it almost works(except that only one local client receive the message).
private static void SendManuallyOnAllCards(int port, string multicastAddress, IEnumerable<IPAddress> ipAddresses)
{
foreach (IPAddress remoteAddress in ipAddresses)
{
IPAddress ipToUse = remoteAddress;
using (var mSendSocket = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp))
{
mSendSocket.SetSocketOption(SocketOptionLevel.IP, SocketOptionName.AddMembership,
new MulticastOption(IPAddress.Parse(multicastAddress)));
mSendSocket.SetSocketOption(SocketOptionLevel.IP, SocketOptionName.MulticastTimeToLive, 255);
mSendSocket.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReuseAddress, true);
var ipep = new IPEndPoint(ipToUse, port);
//IPEndPoint ipep = new IPEndPoint(IPAddress.Parse(multicastAddress), port);
mSendSocket.Bind(ipep);
mSendSocket.Connect(ipep);
byte[] bytes = Encoding.ASCII.GetBytes("This is my welcome message");
mSendSocket.Send(bytes, bytes.Length, SocketFlags.None);
}
}
}
EDIT 5
Here is the result of my route print(Didn't know that command), and on my two IPs, I always receive data on the 10.9.10.234
Edit 6
I tried several other things:
Use a socket to receive instead of the UdpClient --> Didn't worked
Set some addition socketOption on the reader(DontRoute =1, Broadcast=1) -->Didn't worked
Specify the MulticastInterface that the reader Socket has to use(using socketOption MulticastInterface) --> Didn't work
I had the same problem that I wanted to receive multicasts from all my network interfaces. As EJP already said, you need to call JoinMulticastGroup(IPAddress multicastAddr, IPAddress localAddress) on the UdpClient for all network interfaces:
int port = 1036;
IPAddress multicastAddress = IPAddress.Parse("239.192.1.12");
client = new UdpClient(new IPEndPoint(IPAddress.Any, port));
// list of UdpClients to send multicasts
List<UdpClient> sendClients = new List<UdpClient>();
// join multicast group on all available network interfaces
NetworkInterface[] networkInterfaces = NetworkInterface.GetAllNetworkInterfaces();
foreach (NetworkInterface networkInterface in networkInterfaces)
{
if ((!networkInterface.Supports(NetworkInterfaceComponent.IPv4)) ||
(networkInterface.OperationalStatus != OperationalStatus.Up))
{
continue;
}
IPInterfaceProperties adapterProperties = networkInterface.GetIPProperties();
UnicastIPAddressInformationCollection unicastIPAddresses = adapterProperties.UnicastAddresses;
IPAddress ipAddress = null;
foreach (UnicastIPAddressInformation unicastIPAddress in unicastIPAddresses)
{
if (unicastIPAddress.Address.AddressFamily != AddressFamily.InterNetwork)
{
continue;
}
ipAddress = unicastIPAddress.Address;
break;
}
if (ipAddress == null)
{
continue;
}
client.JoinMulticastGroup(multicastAddress, ipAddress);
UdpClient sendClient = new UdpClient(new IPEndPoint(ipAddress, port));
sendClients.Add(sendClient);
}
I am also creating a list of UdpClients so I can send my multicasts on all network interfaces.
I finally found how to do it!
In fact, if I keep exactly the same code, but using it with async methods, it work!!! I just can't understand why it doesn't work with sync method(if someone knows, you're welcome to tell me :) )
Since I've lost 3 days on this, I think it worth an example:
private static void ReceiveAsync(int port, string address, IEnumerable<IPAddress> localAddresses)
{
IPAddress multicastAddress = IPAddress.Parse(address);
foreach (IPAddress localAddress in localAddresses)
{
var udpClient = new UdpClient(AddressFamily.InterNetwork);
udpClient.Client.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReuseAddress, true);
udpClient.Client.Bind(new IPEndPoint(localAddress, port));
udpClient.JoinMulticastGroup(multicastAddress, localAddress);
udpClient.BeginReceive(OnReceiveSink,
new object[]
{
udpClient, new IPEndPoint(localAddress, ((IPEndPoint) udpClient.Client.LocalEndPoint).Port)
});
}
}
And the async method:
private static void OnReceiveSink(IAsyncResult result)
{
IPEndPoint ep = null;
var args = (object[]) result.AsyncState;
var session = (UdpClient) args[0];
var local = (IPEndPoint) args[1];
byte[] buffer = session.EndReceive(result, ref ep);
//Do what you want here with the data of the buffer
Console.WriteLine("Message received from " + ep + " to " + local);
//We make the next call to the begin receive
session.BeginReceive(OnReceiveSink, args);
}
I hope that helps ;)
You need to join the multicast group via all available interfaces. By default, the outgoing IGMP JOIN message will be routed according to the unicast routing tables, which will send it out via the 'cheapest' route, using whichever NIC accesses that route. If your multicast group can be sourced via more than one of those routes, you need to iterate.

C# IPv4/IPv6 agnostic socket listener

I'm trying to to figure out how to create a protocol agnostic socket listener in C# - it should grab IPv4 and IPv6 requests. Everything I can find on Google seems to be C. Attempting something similar to what I saw for C, I tried the following code:
/*Socket*/ m_sock = null;
/*IPAddress*/ m_addr = null;
/*int*/ m_port = port; /*port passed to function*/
/*int*/ m_listenqueue = listen_queue_size; /*also passed to function, number of pending requests to allow before busy*/
IPAddress[] addrs = Dns.GetHostEntry("localhost").AddressList;
if(family == null) m_addr = addrs[0];
else
{
foreach(IPAddress ia in addrs)
{
if(ia.AddressFamily == family) /*desired address family also passed as an argument*/
{
m_addr = ia;
break;
}
}
}
if(m_addr == null) throw new Exception(this.GetType().ToString() + ".#CONSTRUCTOR#: Listener Initailization Error, couldn't get a host entry for 'localhost' with an address family of " + family.ToString());
m_sock = new Socket(m_addr.AddressFamily, SocketType.Stream, ProtocolType.IP);
/*START "AGNOSTICATION LOGIC"... Tried here...*/
if(m_addr.AddressFamily == AddressFamily.InterNetworkV6) //allow IP4 compatibility
{
m_sock.SetSocketOption(SocketOptionLevel.Tcp, SocketOptionName.AcceptConnection, true);
/*fails*/ m_sock.SetSocketOption(SocketOptionLevel.IP, SocketOptionName.AcceptConnection, true);
/*fails*/ m_sock.SetSocketOption(SocketOptionLevel.IPv6, SocketOptionName.AcceptConnection, true);
}
/*END "AGNOSTICATION LOGIC" */
IPEndPoint _endpoint = new IPEndPoint(m_addr, m_port);
m_sock.Bind(_endpoint);
/*... tried here*/
m_sock.Listen(m_listenqueue);
/*... and tried here*/
I've tried the logic at the three places marked, and regardless of where I put it, the two listed lines will throw an invalid argument exception.
Can anyone recommend to me how I should make a socket that will listen to both IPv4/IPv6?
You can use sock.SetSockOption(SocketOptionLevel.IPv6, SocketOptionName.IPV6Only, 0); to set the socket to allow connections with other protocols than IPv6. It will work from Vista onwards.
SocketOptionName documentation

Categories

Resources