21

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

4

12 回答 12

19

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

问题是计算机可能没有外部 IP 地址。例如,我的笔记本电脑有一个由路由器分配的内部 IP 地址 (192.168.xy)。路由器本身有一个内部 IP 地址,但它的“外部”IP 地址也是内部的。它仅用于与 DSL 调制解调器通信,后者实际上具有外部的、面向 Internet 的 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 领域(或者至少,使您的应用程序更难部署)。

于 2008-09-15T20:20:21.343 回答
6

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

所以:

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

编辑:

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

于 2008-09-15T20:06:38.007 回答
6

我找到了一种对我很有效的方法。通过在我可以访问 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 发送回客户端。

于 2009-08-23T20:53:17.653 回答
5

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

如果你想要一些不依赖别人的东西把你自己的页面http://www.unkwndesign.com/ip.php只是一个快速脚本:

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

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

于 2008-09-15T20:06:33.560 回答
4

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

于 2008-09-15T20:13:42.997 回答
3

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

于 2008-09-15T20:22:44.850 回答
2

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

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

于 2008-09-15T20:10:31.960 回答
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().

于 2009-05-29T20:53:47.010 回答
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);
}

祝你好运。

于 2008-09-15T22:34:57.590 回答
0

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

于 2008-09-15T20:18:23.907 回答
-1

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

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

于 2008-09-15T20:05:32.597 回答
-3

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

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

于 2008-09-15T20:07:01.687 回答