Monday, 16 January 2012

Finding the external IP Address of your machine, with a timeout, in C#

If you try to find the IP Address your machine is using, you can follow two paths. The most obvious one is:
 string host = System.Net.Dns.GetHostName();
System.Net.IPHostEntry ipEntry = System.Net.Dns.GetHostEntry(host);
System.Net.IPAddress[] addr = ipEntry.AddressList;
 for (int i = 0; i < addr.Length; i++)
{
      if (addr[i].AddressFamily != System.Net.Sockets.AddressFamily.InterNetwork)
          continue;
      return addr[i].ToString();
}
 
 return "";
Unfortunately, in most cases people are connected to the internet through a router, and therefore each machine is assigned with an internal IP Address of the private LAN network, assigning the external IP Address to the router only, not to each machine. Finding this external address directly is not trivial, as you would need to deal with the router itself, with different router models, etc.
The easiest way to find the external IP Address is to do a web request to one of the several websites out there specially designed to provide you with that address, like whatismyipaddress.com, www.whatismyip.com, etc. In this case, we will use the last one.
Of course, doing a web request can take time, specially if there is no internet connection available, as the default timeout can take several seconds. This function will do the job for you, and accept a custom timeout.
/// <summary>
/// This function uses a Web request to find the external IP Address your machine is using
/// </summary>
/// <param name="pTimeOutMiliSeconds">Number of miliseconds to wait for a response</param>
/// <returns></returns>
public static string GetExternalIP(int pTimeOutMiliSeconds)
{
   string whatIsMyIp = "http://automation.whatismyip.com/n09230945.asp";
   WebClient wc = new WebClient();
   UTF8Encoding utf8 = new UTF8Encoding();
   try
   {
      string ipaddr = null;
      bool done = false;
 
      wc.DownloadDataCompleted += new
      DownloadDataCompletedEventHandler((object sender, DownloadDataCompletedEventArgs e) =>
      {
         ipaddr = utf8.GetString(e.Result);
         done = true;
      });
 
      wc.DownloadDataAsync(new Uri(whatIsMyIp));
      System.DateTime startTime = System.DateTime.Now;
      while (!done)
      {
         System.TimeSpan sp = System.DateTime.Now - startTime;
 
         // We should get a response in less than timeout. If not, we cancel all and return the internal IP Address
         if (sp.TotalMilliseconds > pTimeOutMiliSeconds)
         {
            done = true;
            wc.CancelAsync();
         }
      }
      return ipaddr;
   }
   catch
   {
      return null;
   }
   finally
   {
      if (wc != null)
      {
         wc.Dispose();
         wc = null;
      }               
   }
}

It works pretty obviously. It just uses the DownloadDataAsync method, instead of the synchronous DownloadData, and waits for a certain amount of time. If no response is received in that time, cancels the async method and returns null.
Hope it helps!

No comments: