通过C#远程获取外部IP地址
我需要找出正在运行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发送回客户端。