C# UDP multicast receive not working (async or sync) - c#

I'm adding a multicast listening feature for an application I'm developing. A device I'm connecting to will send out a multicast packet every second in certain scenarios, and I'd like the user to be able to listen for that packet (if it's being sent).
I can see the multicast packets being continuously sent from the device using Wireshark, so I know they exist and I know I'm using the correct multicast group and port number. I've tried dozen of different ways without any luck to get my application to capture the packets. If I send a test multicast packet from the application itself it receives it no problem. I've tried to receive the packets both async and sync, no change. I'm really stumped on this one and not sure what I'm doing wrong. Every example I've found leads me to believe this should be working.
My multicast udp listener class:
class MulticastClient
{
private UdpClient client;
private IPEndPoint localEp;
private IPAddress multicastAddress;
public byte[] receivedData { get; private set; }
public MulticastClient(string multicastIP, int port)
{
localEp = new IPEndPoint(IPAddress.Any, port);
client = new UdpClient(AddressFamily.InterNetwork);
client.Client.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReuseAddress, true);
client.ExclusiveAddressUse = false;
client.Client.Bind(localEp);
this.multicastAddress = IPAddress.Parse(multicastIP);
client.JoinMulticastGroup(this.multicastAddress);
}
public async Task Listen()
{
UdpReceiveResult result = await client.ReceiveAsync();
receivedData = result.Buffer;
}
}
The button click that kicks it off:
MulticastClient client = new MulticastClient("239.255.0.1", 32768);
await Task.Run(async () =>
{
await client.Listen();
});
byte[] receivedData = client.receivedData;
if (receivedData != null)
{
//Here I display useful information about the packet to the user
}
Here is a snippet of a packet capture, showing the multicast packets that I can't seem to receive:
Wireshark packet capture

Got it to work using a variation of this post.
#jdweng made me realize it when he/she asked about multiple adapters, I had several virtual NICs for VMs that were likely joining ht multicast group instead of my actual hardware NIC.

Related

How to keep a TCP connection open and perform multiple Writes/Reads in C# .NET?

There are multiple posts that describe the performance benefit of keeping a TCP connection open, instead of closing and opening each time you need to read or write. For example:
Best practice: Keep TCP/IP connection open or close it after each transfer?
I'm communicating with an RPC based device that takes json commands. The example I have from the device vendor opens and closes a connection each time they send a command. This is what I currently do via TcpClient in a using statement, but I'd like to see if there's anyway I could improve upon what I've already done. In fact, I had attempted this when starting the project, but couldn't figure out how to do so, so closed each time out of frustration and necessity. My latest experiment using sockets because all posts indicate doing so as a necessity for lower level control:
public class Connection
{
private Socket tcpSocket = null;
public string IpAddress = "192.168.0.30";
public int Port = 50002;
public Connection(string ipAddress, int port)
{
this.IpAddress = ipAddress;
this.Port = port;
}
public void Connect()
{
DnsEndPoint ipe = new DnsEndPoint(this.IpAddress, this.Port);
Socket tempSocket =
new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
tempSocket.Connect(ipe);
if (tempSocket.Connected)
{
this.tcpSocket = tempSocket;
this.tcpSocket.NoDelay = true;
this.tcpSocket.
//this.tcpSocket.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.KeepAlive,true);
Console.WriteLine("Successfully connected.");
}
else
{
Console.WriteLine("Error.");
}
}
public void Disconnect()
{
this.tcpSocket.Disconnect(true);
this.tcpSocket.Dispose();
Console.WriteLine("Successfuly closed.");
}
public string SendCommand()
{
string path = #"C:\Users\me\Desktop\request.json";
string request = File.ReadAllText(path);
Byte[] bytesSent = Encoding.UTF8.GetBytes(request);
this.tcpSocket.Send(bytesSent);
this.tcpSocket.Shutdown(SocketShutdown.Send);
var respBytes = ReceiveAll();
string s = System.Text.Encoding.UTF8.GetString(respBytes, 0, respBytes.Length);
return s;
}
public byte[] ReceiveAll()
{
var buffer = new List<byte>();
var currByte = new Byte[1];
var byteCounter = this.tcpSocket.Receive(currByte, currByte.Length, SocketFlags.None);
while (this.tcpSocket.Available > 0)
{
currByte = new Byte[1];
byteCounter = this.tcpSocket.Receive(currByte, currByte.Length, SocketFlags.None);
if (byteCounter.Equals(1))
{
buffer.Add(currByte[0]);
}
}
return buffer.ToArray();
}
}
Console app:
static void Main(string[] args)
{
Connection s = new Connection();
s.Connect();
Console.WriteLine(s.SendCommand());
Console.WriteLine(s.SendCommand());
Thread.Sleep(5000);
s.Disconnect();
Console.ReadKey();
}
This approach works once. The first time I call send command. It doesn't the second time (throws an exception), because I call socket.Shutdown() on Send in my SendCommand(). I do so because of this post:
TCPClient not receiving data
However, there doesn't seem to be a way to re-enable the ability to Send after calling Shutdown(). So now I just don't know if it's even possible to keep a tcp connection open if you have to both read and write. Moreover, I can't really find a useful example online. Does anyone know how to do so in .NET? Is this even possible?
TCP/IP is a streaming protocol. To pass messages with it you need a “framing protocol” so peers can determine when a message is finished.
One simple way to signal the end of a message is to close the socket when you’ve sent the last byte. But this prevents socket reuse. See the evolution of HTTP for an example of this.
If this is what your device does, there’s no way to reuse a socket.
If it is possible to keep the connection open for more messages depends on the application protocol. There is no way to enforce this if the protocol does not supports it. Thus, ask the vendor or look into the protocol specification (if it exists) for information if and how this is supported.
However, there doesn't seem to be a way to re-enable the ability to Send after calling Shutdown().
There is no way. TCP write shutdown means that one does not want to send any more information. It is impossible to take this back. If the protocol supports multiple message exchanges then it needs to have a different way to detect the end of a message than calling Shutdown.

Listen To Specific Port With UDPClient & Get Transmitted Packets

First of all, I apologize for the wrong usage of terminology.
I have a sensor on my local network. It broadcasts current temperature values to everyone on the network on port 35333. I want to create a C# console program that continuously receives packets from this sensor.
This is my current code:
public static UdpClient Client = new UdpClient(35333);
private static async void Start()
{
Client.BeginReceive(new AsyncCallback(recv), null);
}
private static void recv(IAsyncResult res)
{
IPEndPoint RemoteIpEndPoint = new IPEndPoint(IPAddress.Any, 0);
byte[] received = Client.EndReceive(res, ref RemoteIpEndPoint);
//Process codes
Client.BeginReceive(new AsyncCallback(recv), null);
}
The code above works, however here's the problem: I keep received same byte array all the time.
...
[114][51][57][48][48][77][72][112]
[114][51][57][48][48][77][72][112]
[114][51][57][48][48][77][72][112]
[114][51][57][48][48][77][72][112]
[114][51][57][48][48][77][72][112]
...
As far as I know, and again, excuse my poor networking knowledge, I must somehow send an acknowledgement back to this sensor, so it starts sending me the ''real'' data.
Any tips or suggestions welcomed!
There are at least two possibilities here.
First, this may just be the temperature, and it isn't changing. In that case, you need to parse the bytes in the way that the sensor's spec dictates.
Second, if this is indeed the packet that needs an acknowledgement, then you will need to find out which port the sensor listens on (from the spec), and what the acknowledgement packet should look like (from the spec) and send it to that port.
The key here is reviewing the documents that came with the sensor.
New code would sit inside recv method and appear similar to the following:
private static void recv(IAsyncResult res)
{
IPEndPoint RemoteIpEndPoint = new IPEndPoint(IPAddress.Any, 0);
byte[] received = Client.EndReceive(res, ref RemoteIpEndPoint);
//Pseudo code
//start_packet is the packet of bytes above from the sensor
If (received == start_packet)
{
//send acknowledgement
}
//Process codes
Client.BeginReceive(new AsyncCallback(recv), null);
}

UDP Client after several send/receives stops receiving and blocks port

I'm trying to send and receive to/from a UDP multicast address using UWP. It works perfectly the first few times, but after a while of this send-receive process, it will lock on the receiving part. I changed from an async approach to a synchronous one but still the same. Even if I instantiate a new UDP client, the port is blocked until the app is restarted. Anything I'm doing wrong?
private UdpClient udp;
//inside main function:
if (udp == null)
{
udp = new UdpClient(new IPEndPoint(IPAddress.Any, portNumber));
//^the second time this is called, it will complain about port reuse
udp.Client.ReceiveTimeout = udp.Client.SendTimeout = 3000;
//udp.Client.SetSocketOption(SocketOptionLevel.Udp, SocketOptionName.ReuseAddress, true);
//^invalid
}
//await udp.SendAsync(data, data.Length, , portNumber);
//I changed from async to synchronous in case it was the issue, but no.
udp.Client.SendTo(data, new IPEndPoint(IPAddress.Parse(ipString), portNumber));
//the receive used to be async, too
byte[] receivedByte = new byte[udp.Client.ReceiveBufferSize];
try
{
udp.Client.Receive(receivedByte);
}
catch (Exception ex)
{
udp.Client.Shutdown(SocketShutdown.Both);
udp = null; // added these, but port still blocked until restart
}
I'm using UWP, and there are methods on class library that aren't here.
After putting UdpClient in a using () statement instead of declaring it as a private field, and limiting its scope by putting it in a short async method, I am not having these problems anymore.

Multiple applications listening on the same UDP port: broadcast IP VS non-broadcast IP wrt which gets the message

Apps A and B are both listening to and sending from UDP port 1337 on the same computer.
App A was started first (so it bound to port 1337 first).
When they each send a message to 127.255.255.255:1337 or 255.255.255.255:1337, both apps A and B get the each others' messages, as well as their own echo.
When they each send a message to 127.0.0.1:1337 or 192.168.1.65:1337, a non-broadcast address, only A gets B's message and its own echo. B cannot even hear its own message. (192.168.1.65 is my computer's network IP where both apps are running)
If I stop app A, B can hear its own message. If I start A again, B can hear both messages and A can head nothing.
Why do messages get duplicated when sent to a broadcast IP, whereas they are distributed on a first-come-first-serve basis if they are sent to a non-broadcast address? Is there a why to get a consistent behavior (set some "always-duplicate" flag)?
Here is some C# code that demonstrates this:
public class Program
{
public static void Main()
{
var udp = new Udp("255.255.255.255", 1337);
Task.Run(() =>
{
while (true)
{
Console.WriteLine(udp.Receive());
}
});
Task.Run(() =>
{
while (true)
{
Thread.Sleep(1000);
udp.Send("(((1)))");
}
});
Console.ReadLine();
}
}
public class Udp
{
private readonly UdpClient _sender;
private readonly UdpClient _listener;
public Udp(string address, int port)
{
_sender = new UdpClient(address, port);
_listener = new UdpClient();
_listener.Client.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReuseAddress, true);
_listener.Client.Bind(new IPEndPoint(IPAddress.Any, port));
}
public string Receive()
{
var _ = null as IPEndPoint;
return $"{Encoding.Default.GetString(_listener.Receive(ref _))} from {_.Address}:{_.Port}";
}
public void Send(string message)
{
var dataAsBytes = Encoding.ASCII.GetBytes(message);
_sender.Send(dataAsBytes, dataAsBytes.Length);
}
}
You can run this app twice, change the number in udp.Send("(((1)))") to identify each. When you use new Udp("255.255.255.255", 1337) both instances can hear both messages, whereas using new Udp("127.0.0.1", 1337) causes the first instance only to be able to pick up messages. Thank you!

UDP multicasting with multiple NICs only works when one interface is active

I have looked all over and cannot find a solution to this problem. I have tried every combination I could see with no luck.
Basically, I would like to choose an interface, start a UDP client on two machines and Send/Receive messages. Everything works fine when only one NIC is active, but when two are active, it stops working. I have looked with Wireshark and with one NIC can see packets coming in and going out.
Now when I use two NICs, I can only TX from the first enumerated one and cannot receive on either. WireShark does not show any received packets on the port for either of the two NICs when they are both active.
The code is the following. I used to just have one socket but was trying some different things.
public UDPInstance(IPAddress ip, int port, int RXFrequency)
{
rxFreq = RXFrequency;
// Listener Init
TXclient = new UdpClient();
RXclient = new UdpClient();
TXclient.ExclusiveAddressUse = false;
RXclient.ExclusiveAddressUse = false;
//localEp = new IPEndPoint(ip, port);
TXlocalEp = new IPEndPoint(ip, port);
RXlocalEp = new IPEndPoint(IPAddress.Any, port);
TXclient.Client.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReuseAddress, true);
RXclient.Client.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReuseAddress, true);
TXclient.Client.Bind(TXlocalEp);
RXclient.Client.Bind(RXlocalEp);
InterfaceIP = ip.ToString();
multicastaddress = IPAddress.Parse("239.0.0.222");
TXclient.JoinMulticastGroup(multicastaddress);
RXclient.JoinMulticastGroup(multicastaddress);
// Sender Init
remoteep = new IPEndPoint(multicastaddress, port);
Listener = null;
RXData = new List<string>();
StartListenerThread();
}
public void StartListenerThread()
{
Listener = new Thread(new ThreadStart(ListenerThread));
Listener.IsBackground = true;
Listener.Start();
}
public void StopListenerThread()
{
Listener.Abort();
}
private void ListenerThread()
{
while (true)
{
Byte[] data = RXclient.Receive(ref remoteep);
string datastr = Encoding.Unicode.GetString(data);
if (datastr != "")
{
string[] PacketStrings = datastr.Split(new char[] { '~' });
foreach (string pkt in PacketStrings)
RXData.Add(pkt);
}
Thread.Sleep(rxFreq);
}
}
public void Transmit(string data)
{
byte[] buffer;
buffer = Encoding.Unicode.GetBytes(data);
TXclient.Send(buffer, buffer.Length, remoteep);
}
Mike G is correct. One of the constructors for the UDPClient class takes an IPEndPoint as an argument. If the IPEndPoint is set to the IP address of a local interface, then that is the interface that the UDPClient and underlying socket will use so yes, you can have two UDP clients bound to the same port on a a machine as long as they are on seperate local IP interfaces (i.e. multi-homed or multi-NIC).
I know this thread is old, but having the same problem, I thought I would contribute anyway.
On my 'sender' machine, I have 6 NICs. But only 1 needs to be able to send multicast messages, so I used this trick from http://sinclairmediatech.com/using-multicast-on-windows-with-multiple-nics/ :
A little trick I use to make sure I am getting the multicast on the
right interface.
Open cmd as administrator (right click run as administrator)
Delete the default multicast routes. > route delete 224.0.0.0 mask 240.0.0.0
Add the route to the NIC you want. > route add 224.0.0.0 mask 240.0.0.0 IP_of_NIC
I had the same issue on a windows failover cluster...
Multiple nics....
I ended up opening a case with Micorsoft as I thought it was an OS issue.
It wasn't.
You need to specify the IP of the interface you whant to use to create a IPEndpoint.
THen use that endpoint when creating the socket instead of IPAddress.any
That solved the problem for me.
Hope it helps even if it is late.

Categories