获取公共/外部 IP 地址?

我似乎不能得到或找到有关寻找我的路由器公共 IP 的信息?这是因为它不能这样做,将不得不从一个网站?

193341 次浏览

In theory your router should be able to tell you the public IP address of the network, but the way of doing this will necessarily be inconsistent/non-straightforward, if even possible with some router devices.

The easiest and still a very reliable method is to send a request to a web page that returns your IP address as the web server sees it. Dyndns.org provides a good service for this:

http://checkip.dyndns.org/

What is returned is an extremely simple/short HTML document, containing the text Current IP Address: 157.221.82.39 (fake IP), which is trivial to extract from the HTTP response.

static void Main(string[] args)
{
HTTPGet req = new HTTPGet();
req.Request("http://checkip.dyndns.org");
string[] a = req.ResponseBody.Split(':');
string a2 = a[1].Substring(1);
string[] a3=a2.Split('<');
string a4 = a3[0];
Console.WriteLine(a4);
Console.ReadLine();
}

Do this small trick with Check IP DNS

Use HTTPGet class i found on Goldb-Httpget C#

Using a great similar service

private string GetPublicIpAddress()
{
var request = (HttpWebRequest)WebRequest.Create("http://ifconfig.me");


request.UserAgent = "curl"; // this will tell the server to return the information as if the request was made by the linux "curl" command


string publicIPAddress;


request.Method = "GET";
using (WebResponse response = request.GetResponse())
{
using (var reader = new StreamReader(response.GetResponseStream()))
{
publicIPAddress = reader.ReadToEnd();
}
}


return publicIPAddress.Replace("\n", "");
}

With a few lines of code you can write your own Http Server for this.

HttpListener listener = new HttpListener();
listener.Prefixes.Add("http://+/PublicIP/");
listener.Start();
while (true)
{
HttpListenerContext context = listener.GetContext();
string clientIP = context.Request.RemoteEndPoint.Address.ToString();
using (Stream response = context.Response.OutputStream)
using (StreamWriter writer = new StreamWriter(response))
writer.Write(clientIP);


context.Response.Close();
}

Then anytime you need to know your public ip, you can do this.

WebClient client = new WebClient();
string ip = client.DownloadString("http://serverIp/PublicIP");

With .Net WebRequest:

  public static string GetPublicIP()
{
string url = "http://checkip.dyndns.org";
System.Net.WebRequest req = System.Net.WebRequest.Create(url);
System.Net.WebResponse resp = req.GetResponse();
System.IO.StreamReader sr = new System.IO.StreamReader(resp.GetResponseStream());
string response = sr.ReadToEnd().Trim();
string[] a = response.Split(':');
string a2 = a[1].Substring(1);
string[] a3 = a2.Split('<');
string a4 = a3[0];
return a4;
}

When I debug, I use following to construct the externally callable URL, but you could just use first 2 lines to get your public IP:

public static string ExternalAction(this UrlHelper helper, string actionName, string controllerName = null, RouteValueDictionary routeValues = null, string protocol = null)
{
#if DEBUG
var client = new HttpClient();
var ipAddress = client.GetStringAsync("http://ipecho.net/plain").Result;
// above 2 lines should do it..
var route = UrlHelper.GenerateUrl(null, actionName, controllerName, routeValues, helper.RouteCollection, helper.RequestContext, true);
if (route == null)
{
return route;
}
if (string.IsNullOrEmpty(protocol) && string.IsNullOrEmpty(ipAddress))
{
return route;
}
var url = HttpContext.Current.Request.Url;
protocol = !string.IsNullOrWhiteSpace(protocol) ? protocol : Uri.UriSchemeHttp;
return string.Concat(protocol, Uri.SchemeDelimiter, ipAddress, route);
#else
helper.Action(action, null, null, HttpContext.Current.Request.Url.Scheme)
#endif
}

checkip.dyndns.org is not always works correctly. For example, for my machine it shows internal after-NAT address:

Current IP Address: 192.168.1.120

I think its happening, because of I have my local DNS-zone behind NAT, and my browser sends to checkip its local IP address, which is returned back.

Also, http is heavy weight and text oriented TCP-based protocol, so not very suitable for quick and efficient regular request for external IP address. I suggest to use UDP-based, binary STUN, especially designed for this purposes:

http://en.wikipedia.org/wiki/STUN

STUN-server is like "UDP mirror". You looking to it, and see "how I looks".

There is many public STUN-servers over the world, where you can request your external IP. For example, see here:

http://www.voip-info.org/wiki/view/STUN

You can download any STUN-client library, from Internet, for example, here:

http://www.codeproject.com/Articles/18492/STUN-Client

And use it.

Using C#, With webclient its a short one.

public static void Main(string[] args)
{
string externalIpString = new WebClient().DownloadString("http://icanhazip.com").Replace("\\r\\n", "").Replace("\\n", "").Trim();
var externalIp = IPAddress.Parse(externalIpString);


Console.WriteLine(externalIp.ToString());
}

Command Line (works on both Linux and Windows)

wget -qO- http://bot.whatismyipaddress.com

OR

curl http://ipinfo.io/ip

Fast way to get External ip without any connection Actualy no need any Http connection for that

first you must add NATUPNPLib.dll on Referance And select it from referances and check from properties window Embed Interop Type to False

using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using NATUPNPLib; // Add this dll from referance and chande Embed Interop Interop to false from properties panel on visual studio
using System.Net;


namespace Client
{
class NATTRAVERSAL
{
//This is code for get external ip
private void NAT_TRAVERSAL_ACT()
{
UPnPNATClass uPnP = new UPnPNATClass();
IStaticPortMappingCollection map = uPnP.StaticPortMappingCollection;


foreach (IStaticPortMapping item in map)
{
Debug.Print(item.ExternalIPAddress); //This line will give you external ip as string
break;
}
}
}
}

Or this, it works quite well i think for what i needed. It's from here.

public IPAddress GetExternalIP()
{
WebClient lol = new WebClient();
string str = lol.DownloadString("http://www.ip-adress.com/");
string pattern = "<h2>My IP address is: (.+)</h2>"
MatchCollection matches1 = Regex.Matches(str, pattern);
string ip = matches1(0).ToString;
ip = ip.Remove(0, 21);
ip = ip.Replace("


", "");
ip = ip.Replace(" ", "");
return IPAddress.Parse(ip);
}

I do it using HttpClient from System.Net.Http:

public static string PublicIPAddress()
{
string uri = "http://checkip.dyndns.org/";
string ip = String.Empty;


using (var client = new HttpClient())
{
var result = client.GetAsync(uri).Result.Content.ReadAsStringAsync().Result;


ip = result.Split(':')[1].Split('<')[0];
}


return ip;
}
public string GetClientIp() {
var ipAddress = string.Empty;
if (System.Web.HttpContext.Current.Request.ServerVariables["HTTP_X_FORWARDED_FOR"] != null) {
ipAddress = System.Web.HttpContext.Current.Request.ServerVariables["HTTP_X_FORWARDED_FOR"].ToString();
} else if (System.Web.HttpContext.Current.Request.ServerVariables["HTTP_CLIENT_IP"] != null &&
System.Web.HttpContext.Current.Request.ServerVariables["HTTP_CLIENT_IP"].Length != 0) {
ipAddress = System.Web.HttpContext.Current.Request.ServerVariables["HTTP_CLIENT_IP"];
} else if (System.Web.HttpContext.Current.Request.UserHostAddress.Length != 0) {
ipAddress = System.Web.HttpContext.Current.Request.UserHostName;
}
return ipAddress;
}

works perfect

using System.Net;


private string GetWorldIP()
{
String url = "http://bot.whatismyipaddress.com/";
String result = null;


try
{
WebClient client = new WebClient();
result = client.DownloadString(url);
return result;
}
catch (Exception ex) { return "127.0.0.1"; }
}

Used loopback as fallback just so things don't fatally break.

string pubIp =  new System.Net.WebClient().DownloadString("https://api.ipify.org");

You may use Telnet to programmatically query your router for the WAN IP.

The Telnet part

The Telnet part can be accomplished using, for example, this Minimalistic Telnet code as an API to send a Telnet command to your router and get the router's response. The remainder of this answer assumes you are set up in one way or another to send a Telnet command and get back the response in your code.

Limitations of approach

I will say up front that one drawback of querying the router compared to other approaches is that the code you write is likely to be fairly specific to your router model. That said, it can be a useful approach that doesn't rely on external servers, and you may anyway wish to access your router from your own software for other purposes, such as configuring and controlling it, making it more worthwhile writing specific code.

Example router command and response

The example below will not be right for all routers, but illustrates the approach in principle. You will need to change the particulars to suit your router commands and responses.

For example, the way to get your router to show the WAN IP may be the following Telnet command:

connection list

The output may consist of a list of lines of text, one per connection, with the IP address at offset 39. The line for the WAN connection may be identifiable from the word "Internet" somewhere in the line:

  RESP: 3947  17.110.226. 13:443       146.200.253. 16:60642     [R..A] Internet      6 tcp   128
<------------------  39  -------------><--  WAN IP -->

The output may pad each IP address segment out to three characters with spaces, which you will need to remove. (That is, in the xample above, you would need to turn "146.200.253. 16" into "146.200.253.16".)

By experimentation or consulting reference documentation for your router, you can establish the command to use for your specific router and how to interpret the router's response.

Code to get the WAN IP

(Assumes you have a method sendRouterCommand for the Telnet part—see above.)

Using the example router described above, the following code gets the WAN IP:

private bool getWanIp(ref string wanIP)
{
string routerResponse = sendRouterCommand("connection list");


return (getWanIpFromRouterResponse(routerResponse, out wanIP));
}


private bool getWanIpFromRouterResponse(string routerResponse, out string ipResult)
{
ipResult = null;
string[] responseLines = routerResponse.Split(new char[] { '\n' });


//  RESP: 3947  17.110.226. 13:443       146.200.253. 16:60642     [R..A] Internet      6 tcp   128
//<------------------  39  -------------><---  15   --->


const int offset = 39, length = 15;


foreach (string line in responseLines)
{
if (line.Length > (offset + length) && line.Contains("Internet"))
{
ipResult = line.Substring(39, 15).Replace(" ", "");
return true;
}
}


return false;
}

Best answer I found

To get the remote ip address the quickest way possible. You must have to use a downloader, or create a server on your computer.

The downsides to using this simple code: (which is recommended) is that it will take 3-5 seconds to get your Remote IP Address because the WebClient when initialized always takes 3-5 seconds to check for your proxy settings.

 public static string GetIP()
{
string externalIP = "";
externalIP = new WebClient().DownloadString("http://checkip.dyndns.org/");
externalIP = (new Regex(@"\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}"))
.Matches(externalIP)[0].ToString();
return externalIP;
}

Here is how I fixed it.. (first time still takes 3-5 seconds) but after that it will always get your Remote IP Address in 0-2 seconds depending on your connection.

public static WebClient webclient = new WebClient();
public static string GetIP()
{
string externalIP = "";
externalIP = webclient.DownloadString("http://checkip.dyndns.org/");
externalIP = (new Regex(@"\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}"))
.Matches(externalIP)[0].ToString();
return externalIP;
}

Expanding on this answer by @suneel ranga:

static System.Net.IPAddress GetPublicIp(string serviceUrl = "https://ipinfo.io/ip")
{
return System.Net.IPAddress.Parse(new System.Net.WebClient().DownloadString(serviceUrl));
}

Where you would use a service with System.Net.WebClient that simply shows the IP address as a string and uses the System.Net.IPAddress object. Here are a few such services*:

* Some services were mentioned in this question and from these answers from superuser site.

private static string GetPublicIpAddress()
{
using (var client = new WebClient())
{
return client.DownloadString("http://ifconfig.me").Replace("\n", "");
}
}

Most of the answers have mentioned http://checkip.dyndns.org in solution. For us, it didn't worked out well. We have faced Timemouts a lot of time. Its really troubling if your program is dependent on the IP detection.

As a solution, we use the following method in one of our desktop applications:

    // Returns external/public ip
protected string GetExternalIP()
{
try
{
using (MyWebClient client = new MyWebClient())
{
client.Headers["User-Agent"] =
"Mozilla/4.0 (Compatible; Windows NT 5.1; MSIE 6.0) " +
"(compatible; MSIE 6.0; Windows NT 5.1; " +
".NET CLR 1.1.4322; .NET CLR 2.0.50727)";


try
{
byte[] arr = client.DownloadData("http://checkip.amazonaws.com/");


string response = System.Text.Encoding.UTF8.GetString(arr);


return response.Trim();
}
catch (WebException ex)
{
// Reproduce timeout: http://checkip.amazonaws.com:81/


// trying with another site
try
{
byte[] arr = client.DownloadData("http://icanhazip.com/");


string response = System.Text.Encoding.UTF8.GetString(arr);


return response.Trim();
}
catch (WebException exc)
{ return "Undefined"; }
}
}
}
catch (Exception ex)
{
// TODO: Log trace
return "Undefined";
}
}

Good part is, both sites return IP in plain format. So string operations are avoided.

To check your logic in catch clause, you can reproduce Timeout by hitting a non available port. eg: http://checkip.amazonaws.com:81/

Basically I prefer to use some extra backups in case if one of IP is not accessible. So I use this method.

 public static string GetExternalIPAddress()
{
string result = string.Empty;
try
{
using (var client = new WebClient())
{
client.Headers["User-Agent"] =
"Mozilla/4.0 (Compatible; Windows NT 5.1; MSIE 6.0) " +
"(compatible; MSIE 6.0; Windows NT 5.1; " +
".NET CLR 1.1.4322; .NET CLR 2.0.50727)";


try
{
byte[] arr = client.DownloadData("http://checkip.amazonaws.com/");


string response = System.Text.Encoding.UTF8.GetString(arr);


result = response.Trim();
}
catch (WebException)
{
}
}
}
catch
{
}


if (string.IsNullOrEmpty(result))
{
try
{
result = new WebClient().DownloadString("https://ipinfo.io/ip").Replace("\n", "");
}
catch
{
}
}


if (string.IsNullOrEmpty(result))
{
try
{
result = new WebClient().DownloadString("https://api.ipify.org").Replace("\n", "");
}
catch
{
}
}


if (string.IsNullOrEmpty(result))
{
try
{
result = new WebClient().DownloadString("https://icanhazip.com").Replace("\n", "");
}
catch
{
}
}


if (string.IsNullOrEmpty(result))
{
try
{
result = new WebClient().DownloadString("https://wtfismyip.com/text").Replace("\n", "");
}
catch
{
}
}


if (string.IsNullOrEmpty(result))
{
try
{
result = new WebClient().DownloadString("http://bot.whatismyipaddress.com/").Replace("\n", "");
}
catch
{
}
}


if (string.IsNullOrEmpty(result))
{
try
{
string url = "http://checkip.dyndns.org";
System.Net.WebRequest req = System.Net.WebRequest.Create(url);
System.Net.WebResponse resp = req.GetResponse();
System.IO.StreamReader sr = new System.IO.StreamReader(resp.GetResponseStream());
string response = sr.ReadToEnd().Trim();
string[] a = response.Split(':');
string a2 = a[1].Substring(1);
string[] a3 = a2.Split('<');
result = a3[0];
}
catch (Exception)
{
}
}


return result;
}

In order to update GUI control (WPF, .NET 4.5), for instance some Label I use this code

 void GetPublicIPAddress()
{
Task.Factory.StartNew(() =>
{
var ipAddress = SystemHelper.GetExternalIPAddress();


Action bindData = () =>
{
if (!string.IsNullOrEmpty(ipAddress))
labelMainContent.Content = "IP External: " + ipAddress;
else
labelMainContent.Content = "IP External: ";


labelMainContent.Visibility = Visibility.Visible;
};
this.Dispatcher.InvokeAsync(bindData);
});


}

Hope it is useful.

Here is an example of app that will include this code.

The IPIFY API is nice, as it can respond in raw text and JSON. It can also do callbacks etc. The only problem is that it responds in IPv4, not 6.

I found that http://checkip.dyndns.org/ was giving me html tags I had to process but https://icanhazip.com/ was just giving me a simple string. Unfortunately https://icanhazip.com/ gives me the ip6 address and I needed ip4. Luckily there are 2 subdomains that you can choose from, ipv4.icanhazip.com and ipv6.icanhazip.com.

        string externalip = new WebClient().DownloadString("https://ipv4.icanhazip.com/");
Console.WriteLine(externalip);
Console.WriteLine(externalip.TrimEnd());

I've refactored @Academy of Programmer's answer to shorter code and altered it so that it only hits https:// URLs:

    public static string GetExternalIPAddress()
{
string result = string.Empty;


string[] checkIPUrl =
{
"https://ipinfo.io/ip",
"https://checkip.amazonaws.com/",
"https://api.ipify.org",
"https://icanhazip.com",
"https://wtfismyip.com/text"
};


using (var client = new WebClient())
{
client.Headers["User-Agent"] = "Mozilla/4.0 (Compatible; Windows NT 5.1; MSIE 6.0) " +
"(compatible; MSIE 6.0; Windows NT 5.1; .NET CLR 1.1.4322; .NET CLR 2.0.50727)";


foreach (var url in checkIPUrl)
{
try
{
result = client.DownloadString(url);
}
catch
{
}


if (!string.IsNullOrEmpty(result))
break;
}
}


return result.Replace("\n", "").Trim();
}
}

Answers based on using external web services are not exactly correct, because they do not actually answer the stated question:

... information on finding my routers public IP


Explanation

All online services return the external IP address, but it does not essentially mean, that this address is assigned to the user's router.

Router may be assigned with another local IP address of ISP infrastructure networks. Practically this means, that router can not host any services available on Internet. This may be good for safety of most home users, but not good for geeks who host servers at home.

Here's how to check if router has external IP:

trace route to web server behind ISP

According to Wikipedia article, the IP address ranges 10.0.0.0 – 10.255.255.255, 172.16.0.0 – 172.31.255.255 and 192.168.0.0 – 192.168.255.255 are used for private i.e. local networks.

See what happens when you trace route to some remote host with router being assigned with external IP address:

trace route to web server with external IP address

Gotcha! First hop starts from 31.* now. This clearly means that there's nothing between your router and Internet.


Solution

  1. Make Ping to some address with Ttl = 2
  2. Evaluate where response comes from.

TTL=2 must be not enough to reach remote host. Hop #1 host will emit "Reply from <ip address>: TTL expired in transit." revealing its IP address.

Implementation

try
{
using (var ping = new Ping())
{
var pingResult = ping.Send("google.com");
if (pingResult?.Status == IPStatus.Success)
{
pingResult = ping.Send(pingResult.Address, 3000, "ping".ToAsciiBytes(), new PingOptions { Ttl = 2 });


var isRealIp = !Helpers.IsLocalIp(pingResult?.Address);


Console.WriteLine(pingResult?.Address == null
? $"Has {(isRealIp ? string.Empty : "no ")}real IP, status: {pingResult?.Status}"
: $"Has {(isRealIp ? string.Empty : "no ")}real IP, response from: {pingResult.Address}, status: {pingResult.Status}");


Console.WriteLine($"ISP assigned REAL EXTERNAL IP to your router, response from: {pingResult?.Address}, status: {pingResult?.Status}");
}
else
{
Console.WriteLine($"Your router appears to be behind ISP networks, response from: {pingResult?.Address}, status: {pingResult?.Status}");
}
}
}
catch (Exception exc)
{
Console.WriteLine("Failed to resolve external ip address by ping");
}

Small helper is used to check if IP belongs to private or public network:

public static bool IsLocalIp(IPAddress ip) {
var ipParts = ip.ToString().Split(new [] { "." }, StringSplitOptions.RemoveEmptyEntries).Select(int.Parse).ToArray();


return (ipParts[0] == 192 && ipParts[1] == 168)
|| (ipParts[0] == 172 && ipParts[1] >= 16 && ipParts[1] <= 31)
||  ipParts[0] == 10;
}

I find most of the other answers lacking as they assume that any returned string must be the IP, but doesn't really check for it. This is my solution that I'm currently using. It will only return a valid IP or null if none is found.

public class WhatsMyIp
{
public static IPAddress PublicIp { get; private set; }
static WhatsMyIp()
{
PublicIp = GetMyIp();
}


public static IPAddress GetMyIp()
{
List<string> services = new List<string>()
{
"https://ipv4.icanhazip.com",
"https://api.ipify.org",
"https://ipinfo.io/ip",
"https://checkip.amazonaws.com",
"https://wtfismyip.com/text",
"http://icanhazip.com"
};
using (var webclient = new WebClient())
foreach (var service in services)
{
try { return IPAddress.Parse(webclient.DownloadString(service)); } catch { }
}
return null;
}
}

I had almost the same as Jesper, only I reused the webclient and disposed it correctly. Also I cleaned up some responses by removing the extra \n at the end.


private static IPAddress GetExternalIp () {
using (WebClient client = new WebClient()) {
List<String> hosts = new List<String>();
hosts.Add("https://icanhazip.com");
hosts.Add("https://api.ipify.org");
hosts.Add("https://ipinfo.io/ip");
hosts.Add("https://wtfismyip.com/text");
hosts.Add("https://checkip.amazonaws.com/");
hosts.Add("https://bot.whatismyipaddress.com/");
hosts.Add("https://ipecho.net/plain");
foreach (String host in hosts) {
try {
String ipAdressString = client.DownloadString(host);
ipAdressString = ipAdressString.Replace("\n", "");
return IPAddress.Parse(ipAdressString);
} catch {
}
}
}
return null;
}


WebClient, WebRequest and many other are obsolete, consider using it:

public static IPAddress? GetExternalIP ()
{
try
{
using (var client = new HttpClient())
return IPAddress.Parse(client.GetAsync("http://ipinfo.io/ip").Result.Content.ReadAsStringAsync().Result);
}
catch (Exception ex)
{
return null;
}
}

Better late than never

    private static string GetLocalAddress()
{
var host = Dns.GetHostEntry(Dns.GetHostName());
foreach (var ip in host.AddressList.Where(ip => ip.AddressFamily == AddressFamily.InterNetwork))
{
if (!string.IsNullOrEmpty(ip.ToString())) return ip.ToString();
}
return string.Empty;
}