在C#中通过远程处理获取外部IP地址

时间:2008-09-15 20:02:29

标签: c# .net remoting

我需要找出运行C#应用程序的计算机的外部 IP。

在应用程序中,我有一个连接(通过.NET远程处理)到服务器。有没有一种方法可以在服务器端获取客户端的地址?

(我已经编辑了这个问题,要更清楚一些。我向所有那些尽力回答这个问题的人道歉,当时我可能有点太模糊了)

解决方案:
我发现了一种对我有用的方法。通过实现我可以访问CommonTransportKeys.IPAddress的自定义IServerChannelSinkProvider和IServerChannelSink,可以很容易地在CallContext上添加客户端ip。

public ServerProcessing ProcessMessage(IServerChannelSinkStack sinkStack, 
    IMessage requestmessage, ITransportHeaders requestHeaders, 
    System.IO.Stream requestStream, out IMessage responseMessage, 
    out ITransportHeaders responseHeaders, out System.IO.Stream responseStream)
{
    try
    {
        // Get the IP address and add it to the call context.
        IPAddress ipAddr = (IPAddress)requestHeaders[CommonTransportKeys.IPAddress];
        CallContext.SetData("ClientIP", ipAddr);
    }
    catch (Exception)
    {
    }

    sinkStack.Push(this, null);
    ServerProcessing srvProc = _NextSink.ProcessMessage(sinkStack, requestmessage, requestHeaders,
        requestStream, out responseMessage, out responseHeaders, out responseStream);

    return srvProc;
}

然后(当我从客户端收到请求时)只需从CallContext获取IP就像这样。

public string GetClientIP()
{
    // Get the client IP from the call context.
    object data = CallContext.GetData("ClientIP");

    // If the data is null or not a string, then return an empty string.
    if (data == null || !(data is IPAddress))
        return string.Empty;

    // Return the data as a string.
    return ((IPAddress)data).ToString();
}

我现在可以将IP发送回客户端。

12 个答案:

答案 0 :(得分:19)

这是你需要深入研究并重新考虑原始问题的问题之一;在这种情况下,“为什么需要外部IP地址?”

问题是计算机可能没有外部IP地址。例如,我的笔记本电脑有一个由路由器分配的内部IP地址(192.168.x.y)。路由器本身具有内部IP地址,但其“外部”IP地址也是内部的。它仅用于与DSL调制解调器通信,DSL调制解调器实际上具有面向互联网的外部IP地址。

所以真正的问题就变成了,“我如何让设备的面向Internet的IP地址跳了2个?”答案通常是,你没有;至少没有使用像你已经被解雇的whatismyip.com这样的服务,或者做一个非常大的黑客攻击,包括将DSL调制解调器密码硬编码到你的应用程序中,查询DSL调制解调器和屏幕抓取管理页面(上帝帮助你如果调制解调器被更换了。)

编辑:现在将此应用于重构的问题,“如何从服务器.NET组件获取客户端的IP地址?”与whatismyip.com一样,服务器能够做的最好的事情是为您提供面向Internet的设备的IP地址,这不太可能是运行该应用程序的计算机的实际IP地址。回到我的笔记本电脑,如果我的面向Internet的IP是75.75.75.75并且LAN IP是192.168.0.112,那么服务器将只能看到75.75.75.75的IP地址。这将是我的DSL调制解调器。如果您的服务器想要单独连接到我的笔记本电脑,我首先需要配置DSL调制解调器和它与我的笔记本电脑之间的任何路由器,以识别来自服务器的传入连接并适当地路由它们。有几种方法可以做到这一点,但它超出了本主题的范围。

如果您实际上是在尝试从服务器连接到客户端,请重新考虑您的设计,因为您正在深入研究WTF领域(或至少使您的应用程序更难部署)。

答案 1 :(得分:6)

Dns.GetHostEntry(Dns.GetHostName());将返回一组IP地址。第一个应该是外部IP,其余的将是NAT背后的。

所以:

IPHostEntry IPHost = Dns.GetHostEntry(Dns.GetHostName());
string externalIP = IPHost.AddressList[0].ToString();

编辑:

据报道,这对某些人不起作用。它对我有用,但可能取决于您的网络配置,它可能无法正常工作。

答案 2 :(得分:5)

最好只使用http://www.whatismyip.com/automation/n09230945.asp它只为自动查找输出IP。

如果你想要一些不依赖别人的东西你自己的页面{{3只是一个快速的脚本:

<?php
echo 'Your Public IP is: ' . $_SERVER['REMOTE_ADDR'];
?>

这里唯一的缺点是它只会检索用于创建请求的接口的外部IP。

答案 3 :(得分:4)

Jonathan Holland的答案基本上是正确的,但值得补充的是Dns.GetHostByName背后的API调用相当耗时,并且最好缓存结果,以便只需要调用一次代码。

答案 4 :(得分:4)

我发现了一种对我有用的方法。通过实现我可以访问CommonTransportKeys.IPAddress的自定义IServerChannelSinkProvider和IServerChannelSink,可以很容易地在CallContext上添加客户端ip。

public ServerProcessing ProcessMessage(IServerChannelSinkStack sinkStack, 
    IMessage requestmessage, ITransportHeaders requestHeaders, 
    System.IO.Stream requestStream, out IMessage responseMessage, 
    out ITransportHeaders responseHeaders, out System.IO.Stream responseStream)
{
    try
    {
        // Get the IP address and add it to the call context.
        IPAddress ipAddr = (IPAddress)requestHeaders[CommonTransportKeys.IPAddress];
        CallContext.SetData("ClientIP", ipAddr);
    }
    catch (Exception)
    {
    }

    sinkStack.Push(this, null);
    ServerProcessing srvProc = _NextSink.ProcessMessage(sinkStack, requestmessage, requestHeaders,
        requestStream, out responseMessage, out responseHeaders, out responseStream);

    return srvProc;
}

然后(当我从客户端收到请求时)只需从CallContext获取IP就像这样。

public string GetClientIP()
{
    // Get the client IP from the call context.
    object data = CallContext.GetData("ClientIP");

    // If the data is null or not a string, then return an empty string.
    if (data == null || !(data is IPAddress))
        return string.Empty;

    // Return the data as a string.
    return ((IPAddress)data).ToString();
}

我现在可以将IP发送回客户端。

答案 5 :(得分:3)

主要问题是公共IP地址不一定与运行应用程序的本地计算机相关。它是从内部网络通过防火墙转换而来的。要在不询问本地网络的情况下真正获取公共IP,就是向互联网页面发出请求并返回结果。如果您不想使用公开的WhatIsMyIP.com类型站点,您可以轻松地创建一个并自己托管 - 最好是作为Web服务,这样您就可以在应用程序中对它进行简单的SOAP兼容调用。您不一定会像幕后发布屏幕捕获那样阅读响应。

答案 6 :(得分:2)

如果您只想要绑定到适配器的IP,可以使用WMI和Win32_NetworkAdapterConfiguration类。

http://msdn.microsoft.com/en-us/library/aa394217(VS.85).aspx

答案 7 :(得分:2)

Patrik的解决方案适合我!

我做了一次重要的改变。在进程消息中,我使用以下代码设置CallContext

// try to set the call context
LogicalCallContext lcc = (LogicalCallContext)requestMessage.Properties["__CallContext"];
if (lcc != null)
{
    lcc.SetData("ClientIP", ipAddr);
}

这会将ip地址放在正确的CallContext中,以便稍后可以检索它 GetClientIP()

答案 8 :(得分:1)

好吧,假设您的客户端已连接System.Net.Sockets.TcpClient,您可以(在服务器上)使用client.Client.RemoteEndPoint。这将为您提供指向客户端的System.Net.EndPoint; 包含System.Net.IPEndPoint子类的实例,但我不确定其条件。转换为此后,您可以检查它的Address属性以获取客户端的地址。

简而言之,我们有

using (System.Net.Sockets.TcpClient client = whatever) {
    System.Net.EndPoint ep = client.Client.RemoteEndPoint;
    System.Net.IPEndPoint ip = (System.Net.IPEndPoint)ep;
    DoSomethingWith(ip.Address);
}
祝你好运。

答案 9 :(得分:0)

我相信理论上你在路由器(例如使用无效的ip范围)的情况下无法使用外部“帮助”而无法做到这一点。

答案 10 :(得分:-2)

您基本上可以通过执行http://whatismyipaddress.com

的WebRequest来解析返回的页面

http://www.dreamincode.net/forums/showtopic24692.htm

答案 11 :(得分:-3)

最可靠的方法是检查http://checkip.dyndns.org/或类似网站,因为在您实际进入网络外部之前,您无法找到外部IP。但是,对这样的URL进行硬编码会要求最终失败。如果当前IP看起来像RFC1918私人地址(192.168.x.x是最熟悉的私有地址,您可能只希望执行此检查。

如果做不到这一点,您可以在防火墙外部实施自己的类似服务,这样您至少可以知道它是否已损坏。