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

发布于 2024-07-05 23:03:42 字数 1650 浏览 7 评论 0原文

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

I need to find out the external IP of the computer a C# application is running on.

In the application I have a connection (via .NET remoting) to a server. Is there a good way to get the address of the client on the server side?

(I have edited the question, to be a little more clear. I'm apologize to all kind people who did their best to respond to the question, when I perhaps was a little too vague)

Solution:
I found a way that worked great for me. By implementing a custom IServerChannelSinkProvider and IServerChannelSink where I have access to CommonTransportKeys.IPAddress, it's easy to add the client ip on the CallContext.

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

And then later (when I get a request from a client) just get the IP from the CallContext like this.

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

I can now send the IP back to the client.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(12

梦行七里 2024-07-12 23:03:42

这是您必须更深入地研究并可能重新思考原始问题的问题之一; 在本例中,“为什么需要外部 IP 地址?”

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

因此,真正的问题变成了“如何获取 2 跳之外的设备面向互联网的 IP 地址?” 答案通常是,你不需要; 至少在没有使用您已经放弃的诸如whatismyip.com之类的服务的情况下,或者进行一次真正大规模的黑客攻击时,包括将DSL调制解调器密码硬编码到您的应用程序中,并查询DSL调制解调器和屏幕抓取管理页面(上帝保佑您)如果调制解调器被更换过)。

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

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

This is one of those questions where you have to look deeper and maybe rethink the original problem; in this case, "Why do you need an external IP address?"

The issue is that the computer may not have an external IP address. For example, my laptop has an internal IP address (192.168.x.y) assigned by the router. The router itself has an internal IP address, but its "external" IP address is also internal. It's only used to communicate with the DSL modem, which actually has the external, internet-facing IP address.

So the real question becomes, "How do I get the Internet-facing IP address of a device 2 hops away?" And the answer is generally, you don't; at least not without using a service such as whatismyip.com that you have already dismissed, or doing a really massive hack involving hardcoding the DSL modem password into your application and querying the DSL modem and screen-scraping the admin page (and God help you if the modem is ever replaced).

EDIT: Now to apply this towards the refactored question, "How do I get the IP address of my client from a server .NET component?" Like whatismyip.com, the best the server will be able to do is give you the IP address of your internet-facing device, which is unlikely to be the actual IP address of the computer running the application. Going back to my laptop, if my Internet-facing IP was 75.75.75.75 and the LAN IP was 192.168.0.112, the server would only be able to see the 75.75.75.75 IP address. That will get it as far as my DSL modem. If your server wanted to make a separate connection back to my laptop, I would first need to configure the DSL modem and any routers inbetween it and my laptop to recognize incoming connections from your server and route them appropriately. There's a few ways to do this, but it's outside the scope of this topic.

If you are in fact trying to make a connection out from the server back to the client, rethink your design because you are delving into WTF territory (or at least, making your application that much harder to deploy).

度的依靠╰つ 2024-07-12 23:03:42

最可靠的方法是检查 http://checkip.dyndns.org/ 等网站或类似,因为直到您真正进入外部网络之前,您无法找到您的外部 IP。 然而,对这样的 URL 进行硬编码最终会导致失败。 您可能希望仅在当前 IP 类似于 RFC1918 私有地址时才执行此检查(192.168.xx 是其中最熟悉的。

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

The most reliable manner of doing this is checking a site like http://checkip.dyndns.org/ or similar, because until you actually go external to your network, you cannot find your external IP. However, hardcoding such a URL is asking for eventual failure. You may wish to only perform this check if the current IP looks like an RFC1918 private address (192.168.x.x being the most familiar of these.

Failing that, you can implement your own, similar, service sitting external to the firewall, so you will at least know if it's broken.

带上头具痛哭 2024-07-12 23:03:42

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

I believe theoretically you are unable to do such a thing while being behind a router (e.g. using invalid ip ranges) without using an external "help".

泪意 2024-07-12 23:03:42

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

Jonathan Holland's answer is fundamentally correct, but it's worth adding that the API calls behind Dns.GetHostByName are fairly time consuming and it's a good idea to cache the results so that the code only has to be called once.

傾城如夢未必闌珊 2024-07-12 23:03:42

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

The main issue is the public IP address is not necessarily correlated to the local computer running the application. It is translated from the internal network through the firewall. To truly obtain the public IP without interrogating the local network is to make a request to an internet page and return the result. If you do not want to use a publicly available WhatIsMyIP.com type site you can easily create one and host it yourself - preferably as a webservice so you can make a simple soap compliant call to it from within your application. You wouldn't necessarily do a screen capture as much as a behind the scenes post and read the response.

秋意浓 2024-07-12 23:03:42

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

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

If you just want the IP that's bound to the adapter, you can use WMI and the Win32_NetworkAdapterConfiguration class.

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

很快妥协 2024-07-12 23:03:42

帕特里克的解决方案对我有用!

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

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

这会将 IP 地址放置在正确的 CallContext 中,以便稍后可以使用以下命令检索它:
GetClientIP()

Patrik's solution works for me!

I made one important change. In process message I set the CallContext using this code:

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

This places the ip address in the correct CallContext, so it can later be retrieved with
GetClientIP().

初心未许 2024-07-12 23:03:42

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

好运。

Well, assuming you have a System.Net.Sockets.TcpClient connected to your client, you can (on the server) use client.Client.RemoteEndPoint. This will give you a System.Net.EndPoint pointing to the client; that should contain an instance of the System.Net.IPEndPoint subclass, though I'm not sure about the conditions for that. After casting to that, you can check it's Address property to get the client's address.

In short, we have

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

Good luck.

來不及說愛妳 2024-07-12 23:03:42

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

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

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

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

Better to just use http://www.whatismyip.com/automation/n09230945.asp it only outputs the IP just for the automated lookups.

If you want something that does not rely on someone else put up your own page http://www.unkwndesign.com/ip.php is just a quick script:

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

The only downside here is that it will only retrieve the external IP of the interface that was used to create the request.

毁梦 2024-07-12 23:03:42

我找到了一种对我来说非常有效的方法。 通过实现自定义 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 发送回客户端。

I found a way that worked great for me. By implementing a custom IServerChannelSinkProvider and IServerChannelSink where I have access to CommonTransportKeys.IPAddress, it's easy to add the client ip on the CallContext.

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

And then later (when I get a request from a client) just get the IP from the CallContext like this.

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

I can now send the IP back to the client.

心如狂蝶 2024-07-12 23:03:42

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

所以:

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

编辑:

有报告称这对某些人不起作用。 它对我有用,但也许根据您的网络配置,它可能不起作用。

Dns.GetHostEntry(Dns.GetHostName()); will return an array of IP addresses. The first one should be the external IP, the rest will be the ones behind NAT.

So:

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

EDIT:

There are reports that this does not work for some people. It does for me, but perhaps depending on your network configuration, it may not work.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文