I have a need to talk to multiple devices on a private LAN using a proprietary UDP protocol. In a production setting we have a industrial PC running a C++ application that can connect to multiple devices and communicate freely with them. I am trying to duplicate that communication using C# for a windows application that technicians, testers and others would be using. I have probably done this myself a thousands times in c++ but c# seems to be giving me all sorts of fits.
I have one UDP listener listening on Any IP and specific Port. This port is different from the device command port. This receives a periodic heartbeat from each device. From this communication I am able to get the specific IP and command Port for that device. The program maintains a list of devices and displays that lists to the user. The user can then select a specific device and inspect it more for more details.
If there are several devices with the same port number the program will not be able to inspect those devices at the same time. When the user selects the device the program creates a new instance of the UdpClient. In pseudo-code form:
udpConnection = new UdpClient();
udpConnection.ExclusiveAddressUse = false;
udpConnection.Client.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReuseAddress, true);
udpConnection.Client.Bind(localEndpoint);
udpConnection.Client.Connect(remoteEndpoint);
Local Endpoint is the Local interface IP and the Device Port Number of the machine the application is running on. Remote EndPoint is the Device IP and Device Port that the application wants to inspect further.
As anyone had similar issues and what have you done to get around it?
Thanks in advance
Update sample network diagram and further explanation:
Network Diagram
In the image I will be able to have two windows viewing Device A and C or B and C simultaneously with no issues. However if I have Device A and C the window that I opened second will not get any communications updates until I close the first window. If I "sniff" the network with WireShark I see all the communications as I would expect.
Code Snippet:
private void ClientThread()
{
IPEndPoint myEndPoint;
myLogger = LogManager.GetLogger("ClientLogs");
myLogger.Debug("Client Started!");
IPAddress ClientAddress = System.Net.IPAddress.Parse(ClientIP);
myIP = "10.0.0.4"; // Currently hard-coded local interface IP
IPAddress thisAddress = System.Net.IPAddress.Parse(myIP);
ClientEndPoint = new IPEndPoint(ClientAddress, Convert.ToInt32(ClientPort));
myEndPoint = new IPEndPoint(thisAddress, Convert.ToInt32(ClientPort)); // Both forms launch but only the first one will receive data.
//myEndPoint = new IPEndPoint(thisAddress, 0); // Both forms will launch but no data received (packets do show being received in wireshark)
//myEndPoint = new IPEndPoint(IPAddress.Any, ClientPort); // Both forms launch but only the first one will receive data.
try
{
myUDP = new UdpClient();
myUDP.Client.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReuseAddress, true); // "Only one usage of each socket address (protocol/network address/port) is normally permitted" Exception when removed.
myUDP.ExclusiveAddressUse = false; // No difference if this is removed
myUDP.Client.Bind(myEndPoint); // Neither form will receive data if this is removed
myUDP.Client.Connect(ClientEndPoint); // removing this will allow the forms to launch but again the only the first will receive data
}
catch (Exception ex)
{
myUDP = null;
myLogger.Fatal(String.Format("Error in setting up UDP Client: {0}", ex.Message));
return;
}
myLogger.Debug(String.Format("Attempt to connect to : {0} on {1}", ClientIP, Convert.ToInt32(ClientPort)));
byte[] bytes = new byte[1452];
try
{
while (ThreadLoop)
{
IPEndPoint newEndPoint = new IPEndPoint(IPAddress.Any, 0);
Byte[] receiveBytes = myUDP.Receive(ref newEndPoint);
ParseProtocolPacket(ref receiveBytes); // Code to parse the data received
}
}
catch (Exception Ex)
{
myLogger.Debug("Client Socket Other Error: " + Ex.Message);
}
}
I think your problem is here:
udpConnection.Client.Bind(localEndpoint);
This line, if I understand correctly, is telling it to use the same local port to make the outbound request. But you already have another UdpClient listening on that port, so Windows won't know which one to send the traffic to (or it just sends it to the first one).
You should be able to just not use Bind() here. Without it, when you call Connect(), it will assign a random local port, which is the way most outbound network requests work.
Or if you need to specify the network interface, then pass 0 for the port and Windows will assign one:
localEndpoint = new IPEndPoint(ipAddress, 0);
Related
I'm trying directly connect (using TCP) two devices that are behind routers implementing NAT. As I see, routers are translating port number of my devices, so port 13000 (port where my app is listening for incoming TCP connections) on my computer will have completly different value as seen publicly. In order to connect two devices, I need to know what the listener's "public port number" is.
I've created "rendezvous server" which is available publicly. Firstly, I've created TCP connection between client A and server. Server extracted port number and passed it to other clients so they could try to connect to client A. But I realized, that when I create new Tcp connection with server, my app creates new port from which it sends packets. So server extracts public port of newly created port used just for connection to that server and not public port of my listener.
So I'm trying to solve it other way. The idea is: I'm sending UDP packets with source port set to port of TcpListener. This way, theoretically, server could extract from incoming message public port of TcpListener (by getting source port from UDP datagram) and pass it to other devices in my network.
// client side
public void AdvertiseListener(IPAddress address, int port, Guid localDeviceId)
{
UdpClient client = new UdpClient(ListenerPort); // changing datagram source to port of my TcpListener
IPEndPoint remoteEP = new IPEndPoint(address, port);
_logger.LogInformation($"Advertising Tcp port to server at {address.MapToIPv4().ToString()}:{port}");
byte[] data = localDeviceId.ToByteArray();
for (int a = 0; a < 10; a++)
{
client.Send(data, data.Length, remoteEP);
}
}
// server side
public void AdvertisementsHandler()
{
AdvertisementListener = new UdpClient(ListenerPort);
AdvertisementListener.BeginReceive(new AsyncCallback(HandleAdverticement), null);
}
private void HandleAdverticement(IAsyncResult ar)
{
IPEndPoint senderIpEndPoint = new IPEndPoint(0, 0); // source address of incoming datagram
var receivedData = AdvertisementListener.EndReceive(ar, ref senderIpEndPoint);
[...]
_logger.LogInformation($"ADVERTICEMENT RECEIVED, port is {senderIpEndPoint.Port}");
try
{
AdvertisementListener.BeginReceive(new AsyncCallback(HandleAdverticement), null);
}
catch (Exception e)
{
_logger.LogError("Advertiser failed");
}
}
The problem is: my server receives not translated number for some reason. senderIpEndPoint in HandleAdverticement equals 13000 which is my private port number. It seems like my router, ironically, leaves port as it is this time. Do you have any ideas of what can I change to solve my problem?
Your TcpListener doesn't have a public port number. If a router is using NAT to change your source port number along the way, your application will not know about it. This is why port-forwarding is required when you want to make a service available to the public.
What you might be looking for is what is known as TCP hole punching. But in most cases that still requires you to have a public third-party public service that can let the two locations exchange their public endpoint information.
Alternatively Universal Plug and Play (UPnP) might allow you to request the router to port-forward automatically. But this is not something I have worked with myself.
I have searched for 2 days and found many, many questions/answers to what appears to be this same issue, with some differences, however none really seem to provide a solution.
I am implementing a library for controlling a DMX system (ColorKinetics devices) directly without an OEM controller. This involves communicating with an Ethernet-enabled power supply (PDS) connected to my home LAN, through a router, which drives the lighting fixtures. The PDS operates on a specific port (6038) and responds to properly formatted datagrams broadcast over the network.
I can successfully broadcast a simple DMX message (Header + DMX data), which gets picked up by the PDS and applied to connected lighting fixtures, so one-way communication is not an issue.
My issue is that I am now trying to implement a device discovery function to detect the PDS(s) and attached lights on the LAN, and I am not able to receive datagrams which are (absolutely) being sent back from the PDS. I can successfully transmit a datagram which instructs the devices to reply, and I can see the reply coming back in WireShark, but my application does not detect the reply.
I also tried running a simple listener app on another machine, which could detect the initial broadcast, but could not hear the return datagram either, however I figure this wouldn't work since the return packet is addressed to the original sender IP address.
I initially tried implementing via UdpClient, then via Sockets, and both produce the same result no matter what options and parameters I seem to specify.
Here is my current, very simple code to test functionality, currently using Sockets.
byte[] datagram = new CkPacket_DiscoverPDSRequestHeader().ToPacket();
Socket sender = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp);
IPEndPoint ep = new IPEndPoint(IPAddress.Parse("192.168.1.149"), 6039);
public Start()
{
// Start listener
new Thread(() =>
{
Receive();
}).Start();
sender.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReuseAddress, true);
sender.EnableBroadcast = true;
// Bind the sender to known local IP and port 6039
sender.Bind(ep);
}
public void Send()
{
// Broadcast the datagram to port 6038
sender.SendTo(datagram, new IPEndPoint(IPAddress.Broadcast, 6038));
}
public void Receive()
{
Socket receiver = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp);
receiver.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReuseAddress, true);
receiver.EnableBroadcast = true;
// Bind the receiver to known local IP and port 6039 (same as sender)
IPEndPoint EndPt = new IPEndPoint(IPAddress.Parse("192.168.1.149"),6039);
receiver.Bind(EndPt);
// Listen
while (true)
{
byte[] receivedData = new byte[256];
// Get the data
int rec = receiver.Receive(receivedData);
// Write to console the number of bytes received
Console.WriteLine($"Received {rec} bytes");
}
}
The sender and receiver are bound to an IPEndPoint with the local IP and port 6039. I did this because I could see that each time I initialized a new UdpClient, the system would dynamically assign an outgoing port, which the PDS would send data back to. Doing it this way, I can say that the listener is definitely listening on the port which should receive the PDS response (6039). I believe that since I have the option ReuseAddress set to true, this shouldn't be a problem (no exceptions thrown).
Start() creates a new thread to contain the listener, and initializes options on the sending client.
Send() successfully broadcasts the 16-byte datagram which is received by the PDS on port 6038, and generates a reply to port 6039 (Seen in WireShark)
Receive() does not receive the datagram. If I bind the listener to port 6038, it will receive the original 16-byte datagram broadcast.
Here is the WireShark data:
Wireshark
I have looked at using a library like SharpPCap, as many answers have suggested, but there appear to be some compatibility issues in the latest release that I am not smart enough to circumvent, which prevent the basic examples from functioning properly on my system. It also seems like this sort of basic functionality shouldn't require that type of external dependency. I've also seen many other questions/answers where the issue was similar, but it was solved by setting this-or-that parameter for the Socket or UdpClient, of which I have tried every combination to no avail.
I have also enabled access permissions through windows firewall, allowed port usage, and even completely disabled the firewall, to no success. I don't believe the issue would be with my router, since messages are getting to Wireshark.
UPDATE 1
Per suggestions, I believe I put the listener Socket in promiscuous mode as follows:
Socket receiver = new Socket(AddressFamily.InterNetwork, SocketType.Raw, ProtocolType.IP);
receiver.SetSocketOption(SocketOptionLevel.IP, SocketOptionName.HeaderIncluded, true);
receiver.EnableBroadcast = true;
IPEndPoint EndPt = new IPEndPoint(IPAddress.Parse("192.168.1.149"), 0);
receiver.Bind(EndPt);
receiver.IOControl(IOControlCode.ReceiveAll, new byte[] { 1, 0, 0, 0 }, null);
This resulted in the listener receiving all sorts of network traffic, including the outbound requests, but still no incoming reply.
UPDATE 2
As Viet suggested, there is some sort of addressing problem in the request datagram, which is formatted as such:
public class CkPacket_DiscoverPDSRequest : BytePacket
{
public uint magic = 0x0401dc4a;
public ushort version = 0x0100;
public ushort type = 0x0100;
public uint sequence = 0x00000000;
public uint command = 0xffffffff;
}
If I change the command field to my broadcast address 192.168.1.149' or192.168.255.255`, my listener begins detecting the return packets. I admittedly do not know what this field is supposed to represent, and my original guess was to just put in a broadcast address since the point of the datagram is to discover all devices on the network. This is obviously not the case, though I am still not sure the exact point of it.
Either way, thank you for the help, this is progress.
So in actuality it turns out that my issue was with the formatting of the outgoing datagram. The command field needs to be an address on the local subnet 192.168.xxx.xxx, and not 255.255.255.255... for whatever reason this was causing the packet to be filtered somewhere before getting to my application, though WireShark could still see it. This may be common sense in this type of work but being relatively ignorant as to network programming as well as the specifics of this interface it wasn't something I had considered.
Making the change allows a simple UdpClient send/receive to function perfectly.
Much thanks to Viet Hoang for helping me find this!
As you've already noted, you don't need to bind to send out a broadcast but it uses a random source port.
If you adjust your code to not bind the sender, your listener should behave as expected again:
Socket sender = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp);
sender.EnableBroadcast = true;
Thread read_thread;
public Start()
{
// Start listener
read_thread = new Thread(Receive);
read_thread.Start();
}
The issue you've bumped into is that the operating system kernel is only delivering packets up to one socket binder (first come first serve basis).
If you want true parallel read access, you'll need to look into sniffing example such as: https://stackoverflow.com/a/12437794/8408335.
Since you are only looking to source the broadcast from the same ip/port, you simply need to let the receiver bind first.
If you add in a short sleep after kicking off the receive thread, and before binding the sender, you will be able to see the expected results.
public Start()
{
// Start listener
new Thread(() =>
{
Receive();
}).Start();
Thread.Sleep(100);
sender.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReuseAddress, true);
sender.EnableBroadcast = true;
// Bind the sender to known local IP and port 6039
sender.Bind(ep);
}
Extra note: You can quickly test your udp sockets from a linux box using netcat:
# echo "hello" | nc -q -1 -u 192.168.1.149 6039 -
- Edit -
Problem Part #2
The source address of "255.255.255.255" is invalid.
Did a quick test with two identical packets altering the source ip:
https://i.stack.imgur.com/BvWIa.jpg
Only the one with the valid source IP was printed to the console.
Received 26 bytes
Received 26 bytes
I'm using a UdpClient to receive and send multicast traffic, but I'm having a problem when a new network interface becomes operational while the application is up and running.
The UdpClient is created in my application when the interface becomes operational (plug in network cable raises NetworkChange.NetworkAddressChanged), it binds to the static IP of the interface, and the expected IGMP packets are visible in wireshark on that interface, but the UdpClient instance never reports there is any data available.
The issue also appears to be present if you create the UdpClient before the cable is connected.
I've tried setting SocketOptionName.MulticastInterface, but that should only concern sending multicast traffic, not receiving... Example for this here: https://support.microsoft.com/en-us/kb/318911
Here is a console app that exhibits this issue. While this app is running, I connect the Ethernet cable and Wireshark shows the IGMP join group packets from this app, and incoming multicast traffic from another computer. If I have the cable already plugged in and start the app, it receives all of the traffic I expect.
class Program
{
static UdpClient udpClient;
static IPAddress bindAddress = IPAddress.Parse("192.168.101.220");
static IPAddress groupListenAddress = IPAddress.Parse("239.255.0.1");
static int port = 9999;
static bool shouldRun = true;
static Thread thread;
static void Main(string[] args)
{
udpClient = new UdpClient();
udpClient.Client.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReuseAddress, true);
udpClient.Client.SetSocketOption(SocketOptionLevel.IP, SocketOptionName.MulticastLoopback, true);
IPEndPoint localEndPoint = new IPEndPoint(bindAddress, port);
udpClient.Client.Bind(localEndPoint);
udpClient.JoinMulticastGroup(groupListenAddress);
thread = new Thread(runThread);
thread.Start();
Console.WriteLine("Press Enter to exit.");
Console.ReadLine();
shouldRun = false;
thread.Join(100);
}
private static void runThread(object obj)
{
while (shouldRun)
{
if (udpClient.Available > 0)
{
IPEndPoint endPoint = null;
byte[] buffer = udpClient.Receive(ref endPoint);
Console.WriteLine("Received Message from: {0} Length: {1}", endPoint, buffer.Length);
}
Thread.Sleep(10);
}
}
}
Note that IGMP messages are sent by your host OS, not by your application. If the OS sees a join for a multicast address for the first time it will send out IGMP messages. If another process wants to join the same address as well, new IGMP message may or may not be sent.
Also all kinds of network components (in particular routers, but also Powerline adapters use IGMP snooping to find out where to send multicast traffic, and they often get it wrong. There are two incompatible versions of IGMP and some devices support only one version and some translate between versions in a strange way. This is a big mess.
To make it all really horrible, most devices, including your local host OS, have a time dependent behavior. This makes analyzing problems really difficult since you can never draw hard conclusions, because at the moment it may work just by chance.
Independent of IGMP, the bind() statement is confusing for UDP. On Linux bindign to an IP address will filter all incoming UDP packets and you will only receive packets going to this specific IP address. To receive multicast traffic on Linux you usually have to bind to 0.0.0.0. But I assume you are using Windows, where binding to an IP address of a local adapter is the right thing. Then you will receive mutilcast traffic going to this adapter.
I have checked and found that the problem is the ip address being assigned to the connectionEndpointPair is carrying the IP of the wi-fi direct network adapter and i don't know how to open the port on that specific ip, the ip is different from when i ping it from my pc the windows is listening on port 5009 and connection established when i use the wi-fi ip but when i use the wi-fi direct ip addresses i'm having an issue
The wi-fi direct connection between the device and the windows 8.1 application is ok, I then am awaiting for my sockets to connect but it does not happen what could be the issue ?
I get error on Visual Studio:
No connection could be made because the target machine actively refused it. (Exception from HRESULT: 0x8007274D)
On the Windows side i am using this code:
String deviceSelector = WiFiDirectDevice.GetDeviceSelector();
DeviceInformationCollection deviceCollection = await DeviceInformation.FindAllAsync(deviceSelector);
if(deviceCollection.Count > 0)
{
try
{
wfdDevice = await WiFiDirectDevice.FromIdAsync(deviceCollection[0].Id);
wfdDevice.ConnectionStatusChanged +=ConnectionStatusChangedNotficationHandler;
var endpointPairs = wfdDevice.GetConnectionEndpointPairs();
EndpointPair connectionEndpointPair = endpointPairs[0];
try
{
connectionEndpointPair.RemoteServiceName = "5009";
StreamSocket socket = new StreamSocket();
await socket.ConnectAsync(connectionEndpointPair);
string s = "hello";
}catch (Exception err)
{
string s = err.Message;
s = err.StackTrace;
}
}
catch (Exception err)
{
string error = err.Message;
}
On the android side i am using this code:
private void initiateClientSocket(String hostAddress) {
int timeout = 10000;
int port = 5009;
InetSocketAddress socketAddress
= new InetSocketAddress(hostAddress, port);
try {
Socket socket = new Socket();
socket.bind(null);
socket.connect(socketAddress, timeout);
} catch (IOException e) {
Log.e(TAG, "IO Exception.", e);
}
// TODO Start Receiving Messages
}
From the android side I am getting:
java.net.UnknownHostException: Host is unresolved: my ip
Can any please help
Thanks :)
Even if you have a direct WIFI Connection between the Android phone and the windows computer, you need a server and a client for a tcp connection.
I don't know the purpose of your application but I assume that the windows computer is the better choice for the server. So instead connecting to the socket, on the computer you should open a listener for the port.
The server class that is related to the chosen StreamSocket class is the StreamSocketListener. You can find the Documentation here: http://msdn.microsoft.com/en-us/library/windows/apps/windows.networking.sockets.streamsocketlistener.aspx
There is also a paragraph in this documentation about the typical order of operations:
Create the StreamSocketListener.
Use the Control property to retrieve a StreamSocketListenerControl object and set the socket quality of service required.
Assign the ConnectionReceived event to an event handler.
Call the BindServiceNameAsync or BindEndpointAsync method to bind to a local TCP port or service name.
When a connection is received, use the StreamSocketListenerConnectionReceivedEventArgs object to retrieve the Socket property with the StreamSocket object created.
Use the StreamSocket object to send and receive data.
Call the Close method to stop listening for and accepting incoming network connections and release all unmanaged resources associated with the StreamSocketListener object. Any StreamSocket objects created when a connection is received are unaffected and can continue to be used as needed.
I haven't worked with this special class, but the basics of TCP are always the same...
I don't quite understand your code on Window, but guess that there is something wrong with the following code
connectionEndpointPair.RemoteServiceName = "5009";
It seems that "RemoteServiceName" has nothing to do with the port number 5009. Please check other fields in the struct/object "connectionEndpointPair" and make proper settings.
I have a simple program I'm developing to perform some bandwidth tests on remote computers my company controls at our client sites. One copy of the program goes on a remote computer and is told to listen on a specified port. Another copy of the same program is then told to connect to the listening computer, then send a serialized object with a message of known length, random contents. The transfer is timed and the results sent back to the originating machine.
It works fine over the loopback interface; two copies of the program going, one listening on a part, the other connecting to that same port (in a shared manner) and sending the data. However, when I move the "remote" side of this test to a different test machine on the same intranet, even though the program says it's "listening" for its connection, the port cannot be connected to by the program on my dev machine. I get the standard error "No connection could be made because the target machine actively refused it".
There are no active firewalls between these two computers, including Windows Firewall. I can create other types of remote connections such as VNC.
Here are the relevant code snippets:
Listening for a connection:
public void BeginListeningAsServer()
{
if (Listener != null) Listener.Stop();
if (ListenerThread != null) ListenerThread.Join();
Listener = new TcpListener(RemoteAddress, RemotePort);
Listener.Start();
Listening = true;
ListenerThread = new Thread(ListenForConnections);
ListenerThread.Start();
}
private void ListenForConnections()
{
while (Listening)
{
if (Listener.Pending())
Listener.BeginAcceptTcpClient(HandleIncomingClient, null);
}
}
private void HandleIncomingClient(IAsyncResult ar)
{
var client = Listener.EndAcceptTcpClient(ar);
var thread = new Thread(c => HandleIncomingClientMessage((TcpClient)c));
HandlerThreads.Add(thread);
Clients.Add(client);
thread.Start(client);
}
Connecting to this listening program:
public void ConnectAsClient()
{
var client = new TcpClient();
client.Client.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReuseAddress, true);
client.Connect(RemoteAddress, RemotePort); //this line fails
Clients.Add(client);
}
The way you star your listener is a bit off. You don't need to specify the Address, only the port.