通过C#远程获取外部IP地址

时间:2020-03-05 18:54:22  来源:igfitidea点击:

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

在应用程序中,我与服务器建立了连接(通过.NET远程处理)。有没有一种好的方法来获取服务器端客户端的地址?

(我已经对问题进行了编辑,以便更加清楚。我向所有想回答这个问题的尽力而为的人道歉,当我可能有点太含糊的时候)

解决方案:
我找到了一种对我有用的方法。通过实现一个自定义的IServerChannelSinkProvider和IServerChannelSink,可以在其中访问CommonTransportKeys.IPAddress,可以很容易地在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发送回客户端。

解决方案

回答

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

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

回答

最好只使用http://www.whatismyip.com/automation/n09230945.asp,它仅输出IP以用于自动查找。
如果我们想要一些不依赖他人的内容,请在我们自己的页面上放置http://www.unkwndesign.com/ip.php只是一个快速脚本:

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

唯一的缺点是,它将仅检索用于创建请求的接口的外部IP。

回答

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

所以:

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

编辑:

有报道说这不适用于某些人。它对我有用,但也许取决于网络配置,它可能无法工作。

回答

执行此操作的最可靠方法是检查http://checkip.dyndns.org/之类的网站或者类似网站,因为在我们真正进入网络外部之前,我们找不到外部IP。但是,对这样的URL进行硬编码会导致最终失败。我们可能希望仅在当前IP看起来像RFC1918专用地址(其中最熟悉的是192.168.x.x)时才执行此检查。

否则,我们可以在防火墙外部实施自己的类似服务,因此至少可以知道它是否已损坏。

回答

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

http://msdn.microsoft.com/zh-CN/library/aa394217(VS.85).aspx

回答

乔纳森·霍兰德(Jonathan Holland)的回答从根本上是正确的,但是值得一提的是,在Dns.GetHostByName之后的API调用相当耗时,并且缓存结果是个好主意,因此只需调用一次代码。

回答

我相信,从理论上讲,如果我们不使用外部"帮助",就无法在路由器后面(例如,使用无效的IP范围)进行此类操作。

回答

这是我们必须更深入研究甚至可能重新思考原始问题的问题之一。在这种情况下,"为什么需要一个外部IP地址?"

问题是计算机可能没有外部IP地址。例如,我的笔记本电脑有一个由路由器分配的内部IP地址(192.168.x.y)。路由器本身有一个内部IP地址,但其"外部" IP地址也是内部的。它仅用于与DSL调制解调器进行通信,而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领域(或者至少使应用程序更难以部署)。

回答

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

回答

好吧,假设我们有一个连接到客户端的" System.Net.Sockets.TcpClient",则可以(在服务器上)使用" client.Client.RemoteEndPoint"。这将为我们提供指向客户端的System.Net.EndPoint。它应该包含System.Net.IPEndPoint子类的实例,尽管我不确定这样做的条件。强制转换后,我们可以检查其"地址"属性以获取客户的地址。

简而言之,我们有

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);
}

祝你好运。

回答

Patrik的解决方案对我有用!

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

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

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

回答

我找到了一种对我有用的方法。通过实现一个自定义的IServerChannelSinkProvider和IServerChannelSink,可以在其中访问CommonTransportKeys.IPAddress,可以很容易地在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发送回客户端。