【问题标题】:Get external IP address over remoting in C#在 C# 中通过远程处理获取外部 IP 地址
【发布时间】:2010-09-09 03:27:39
【问题描述】:

我需要找出运行 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 发送回客户端。

【问题讨论】:

  • 请定义“外部IP”。它是互联网接口吗?另外,是否有保证的连接性?
  • 有保证的连接性。
  • 没有“标准化”的方式来查询另一台机器看到你的流量来自的 IP 地址,而不询问它看到你的流量来自哪里的机器。 Calath21 有正确的答案。
  • 不是很相关,因为我正在寻找一种通过远程处理(而不是屏幕抓取)来做到这一点的方法。不过还是谢谢。

标签: c# .net remoting


【解决方案1】:

你基本上可以解析通过http://whatismyipaddress.com的WebRequest返回的页面

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

【讨论】:

  • 不建议在此处链接到论坛帖子。您可以在此处用引号总结其中的内容,然后提供链接。
【解决方案2】:

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

如果您想要一些不依赖于其他人的东西,请建立您自己的页面 http://www.unkwndesign.com/ip.php 只是一个快速脚本:

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

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

【讨论】:

  • 我无法发布代码,但请在此处查看 forums.microsoft.com/MSDN/…> 以获取有关以编程方式请求和读取 HTTP 请求/响应的代码。
  • 在 C# 中这是字符串 soemthing = Request.ServerVariables["REMOTE_ADDR"];这将返回托管站点的机器的 IP,而不是客户端的 Internet IP 地址。
  • whatismyip 链接已损坏。这就是为什么只使用外部链接并不是更好的原因 - 它使您的软件依赖于完全不受您控制的服务器来完成它不应该依赖它的事情。
【解决方案3】:

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

所以:

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

编辑:

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

【讨论】:

  • 我运行了这段代码,它给了我一个内部 IP。事实上,IPHost.AddressList 包含的所有三个地址都是 192.168.x.x
  • 它确实对我有用,但我猜这取决于您的网络配置,它可能会也可能不会起作用。
  • 比写圣经好。谢谢 FlySwat
  • 不。这是基于您的网络设置,所以这是一个完全不可靠的答案。大多数人将获得私有 IP4 或 IP6 结果。
【解决方案4】:

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

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

【讨论】:

    【解决方案5】:

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

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

    【讨论】:

      【解决方案6】:

      Jonathan Holland 的回答基本正确,但值得补充的是,Dns.GetHostByName 背后的 API 调用相当耗时,最好将结果缓存起来,这样代码只需调用一次。

      【讨论】:

        【解决方案7】:

        我相信理论上你在路由器后面(例如使用无效的 IP 范围)不使用外部“帮助”是无法做这样的事情的。

        【讨论】:

          【解决方案8】:

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

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

          所以真正的问题变成了,“如何获取 2 跳以外的设备的面向 Internet 的 IP 地址?”答案通常是,你不知道;至少在不使用诸如 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 领域(或者至少,使您的应用程序更难部署)。

          【讨论】:

            【解决方案9】:

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

            【讨论】:

              【解决方案10】:

              好吧,假设您有一个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);
              }
              

              祝你好运。

              【讨论】:

                【解决方案11】:

                Patrik 的解决方案对我有用!

                我做了一个重要的改变。在处理消息中,我使用此代码设置了CallContext

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

                这会将 ip 地址放在正确的 CallContext 中,以便以后可以使用 GetClientIP().

                【讨论】:

                  【解决方案12】:

                  我找到了一种非常适合我的方法。通过在我可以访问 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 发送回客户端。

                  【讨论】:

                    猜你喜欢
                    • 1970-01-01
                    • 2021-11-20
                    • 2017-01-26
                    • 2011-06-13
                    • 2018-10-11
                    • 2011-02-25
                    • 1970-01-01
                    • 2013-04-03
                    相关资源
                    最近更新 更多