Drake
Drake

Reputation: 2703

Discover all devices on LAN

In my Xamarin Forms application, I am trying to discover all devices on the local network that I am connected to. My approach is to first get the device IP address, and use to first 3 numbers to know what the gateway is (first number is always 192). And then, ping every address on that gateway. Here is my code:

public partial class MainPage : ContentPage
{
    private List<Device> discoveredDevices = new List<Device>();

    public MainPage()
    {
        InitializeComponent();

        Ping_all();
    }

    private string GetCurrentIp()
    {
        IPAddress[] addresses = Dns.GetHostAddresses(Dns.GetHostName());
        string ipAddress = string.Empty;
        if (addresses != null && addresses[0] != null)
        {
            ipAddress = addresses[0].ToString();
        }
        else
        {
            ipAddress = null;
        }

        return ipAddress;
    }

    public void Ping_all()
    {
        string ip = GetCurrentIp();

        if (ip != null)
        {
            //Extracting and pinging all other ip's.
            string[] array = ip.Split('.');
            string gateway = array[0] + "." + array[1] + "." + array[2];

            for (int i = 2; i <= 255; i++)
            {
                string ping_var = $"{gateway}.{i}";

                //time in milliseconds           
                Ping(ping_var, 4, 4000);
            }
        }
    }

    public void Ping(string host, int attempts, int timeout)
    {
        for (int i = 0; i < attempts; i++)
        {
            new Thread(delegate ()
            {
                try
                {
                    System.Net.NetworkInformation.Ping ping = new System.Net.NetworkInformation.Ping();
                    ping.PingCompleted += new PingCompletedEventHandler(PingCompleted);
                    ping.SendAsync(host, timeout, host);
                    // PingCompleted never gets called
                }
                catch(Exception e)
                {
                    // Do nothing and let it try again until the attempts are exausted.
                    // Exceptions are thrown for normal ping failurs like address lookup
                    // failed.  For this reason we are supressing errors.
                }
            }).Start();
        }
    }

    private void PingCompleted(object sender, PingCompletedEventArgs e)
    {
        string ip = (string)e.UserState;
        if (e.Reply != null && e.Reply.Status == IPStatus.Success)
        {
            string hostname = GetHostName(ip);
            string macaddres = GetMacAddress(ip);

            var device = new Device()
            {
                Hostname = hostname,
                IpAddress = ip,
                MacAddress = macaddres
            };

            discoveredDevices.Add(device);
        }
    }

    public string GetHostName(string ipAddress)
    {
        try
        {
            IPHostEntry entry = Dns.GetHostEntry(ipAddress);
            if (entry != null)
            {
                return entry.HostName;
            }
        }
        catch (SocketException)
        {

        }

        return null;
    }

    public string GetMacAddress(string ipAddress)
    {
        string macAddress = string.Empty;
        System.Diagnostics.Process Process = new System.Diagnostics.Process();
        Process.StartInfo.FileName = "arp";
        Process.StartInfo.Arguments = "-a " + ipAddress;
        Process.StartInfo.UseShellExecute = false;
        Process.StartInfo.RedirectStandardOutput = true;
        Process.StartInfo.CreateNoWindow = true;
        Process.Start();
        string strOutput = Process.StandardOutput.ReadToEnd();
        string[] substrings = strOutput.Split('-');
        if (substrings.Length >= 8)
        {
            macAddress = substrings[3].Substring(Math.Max(0, substrings[3].Length - 2))
                     + "-" + substrings[4] + "-" + substrings[5] + "-" + substrings[6]
                     + "-" + substrings[7] + "-"
                     + substrings[8].Substring(0, 2);
            return macAddress;
        }
        else
        {
            return "OWN Machine";
        }
    }
}

I get to the part where I try to ping:

System.Net.NetworkInformation.Ping ping = new System.Net.NetworkInformation.Ping();
ping.PingCompleted += new PingCompletedEventHandler(PingCompleted);
ping.SendAsync(host, timeout, host);

But PingCompleted never gets called. No exception is thrown either. Any idea why? I'm running this on a physical Android device.

EDIT

PingCompleted started getting called for me now, not sure why it wasn't working before. But it now crashes in my GetMacAddress function on the line Process.Start(); because it can not find the resource.

Upvotes: 3

Views: 9657

Answers (1)

Drake
Drake

Reputation: 2703

I ended up using this really robust and easy to use library:

https://github.com/Yortw/RSSDP

It doesn't actually find all devices on the network, instead it uses SSDP (Simple Search Discovery Protocol) to quickly find all devices that are broadcasting a service with this protocol on the network. I filtered it to only scan devices running my app, which is what I actually needed. It takes only a second to discover my devices, which is much faster than pinging 255 addresses.

In the documentation you will see:

var deviceDefinition = new SsdpRootDevice()
{
    CacheLifetime = TimeSpan.FromMinutes(30), //How long SSDP clients can cache this info.
    Location = new Uri("http://mydevice/descriptiondocument.xml"), // Must point to the URL that serves your devices UPnP description document. 
    DeviceTypeNamespace = "my-namespace",
    DeviceType = "MyCustomDevice",
    FriendlyName = "Custom Device 1",
    Manufacturer = "Me",
    ModelName = "MyCustomDevice",
    Uuid = GetPersistentUuid() // This must be a globally unique value that survives reboots etc. Get from storage or embedded hardware etc.
};

For the Location I set it as my device's IP. So that another device that discovers it can have the IP too. I don't think it's meant to be used this way, but it worked for me and I don't see why not.

I tested it on 2 physical Android devices.

Upvotes: 3

Related Questions