簡體   English   中英

在 C# 中通過遠程獲取外部 IP 地址

[英]Get external IP address over remoting in C#

我需要找出正在運行 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 地址。 例如,我的筆記本電腦有一個由路由器分配的內部 IP 地址(192.168.xy)。 路由器本身有一個內部 IP 地址,但它的“外部” IP 地址也是內部的。 它僅用於與 DSL 調制解調器通信,后者實際上具有外部的、面向互聯網的 IP 地址。

所以真正的問題變成了,“我如何獲得 2 跳外設備的面向 Internet 的 IP 地址?” 答案通常是,你不知道; 至少在不使用諸如 whatismyip.com 之類的服務的情況下,您已經解雇了,或者進行了非常大規模的黑客攻擊,涉及將 DSL 調制解調器密碼硬編碼到您的應用程序中並查詢 DSL 調制解調器和屏幕抓取管理頁面(上帝幫助您如果調制解調器被更換)。

編輯:現在將此應用於重構的問題,“如何從服務器 .NET 組件獲取我的客戶端的 IP 地址?” 就像 whatismyip.com 一樣,服務器能做的最好的事情就是為您提供面向 Internet 的設備的 IP 地址,這不太可能是運行應用程序的計算機的實際 ZA12A3079E14CED46E69BA52B8A90B21 地址。 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. 這樣就可以達到我的 DSL 調制解調器。 如果您的服務器想與我的筆記本電腦建立單獨的連接,我首先需要配置 DSL 調制解調器以及它與我的筆記本電腦之間的任何路由器,以識別來自您的服務器的傳入連接並適當地路由它們。 有幾種方法可以做到這一點,但它不在本主題的 scope 范圍內。

如果您實際上是在嘗試建立從服務器到客戶端的連接,請重新考慮您的設計,因為您正在鑽研 WTF 領域(或者至少,使您的應用程序更難部署)。

Dns.GetHostEntry(Dns.GetHostName()); 將返回一個 IP 地址數組。 第一個應該是外部 IP,rest 將是 NAT 后面的。

所以:

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

編輯:

有報道稱這對某些人不起作用。 它對我有用,但可能取決於您的網絡配置,它可能不起作用。

我找到了一種對我很有效的方法。 通過在我可以訪問 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 發送回客戶端。

最好只使用http://www.whatismyip.com/automation/n09230945.asp它只輸出 IP 僅用於自動查找。

如果你想要一些不依賴別人的東西,貼出你自己的頁面http://www.unkwndesign.com/ip.php只是一個快速腳本:

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

這里唯一的缺點是它只會檢索用於創建請求的接口的外部 IP。

Jonathan Holland 的回答基本上是正確的,但值得補充的是,Dns.GetHostByName 后面的 API 調用相當耗時,緩存結果是個好主意,這樣代碼只需調用一次。

主要問題是公共 IP 地址不一定與運行應用程序的本地計算機相關。 它是通過防火牆從內部網絡翻譯過來的。 在不詢問本地網絡的情況下,真正獲得公共的IP,就是向互聯網頁面發出請求並返回結果。 如果您不想使用公開可用的 WhatIsMyIP.com 類型的站點,您可以輕松地創建一個並自己托管它 - 最好作為 Web 服務,這樣您就可以在您的應用程序中對其進行簡單的 soap 兼容調用。 您不一定會像幕后帖子那樣進行屏幕截圖並閱讀響應。

如果您只想要綁定到適配器的 IP,您可以使用 WMI 和 Win32_NetworkAdapterConfiguration class。

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

Patrik 的解決方案對我有用!

我做了一個重要的改變。 在處理消息中,我使用以下代碼設置了CallContext

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

這會將 ip 地址放在正確的 CallContext 中,以便以后可以使用GetClientIP()檢索它。

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

祝你好運。

我相信理論上你不能在路由器后面做這樣的事情(例如使用無效的 ip 范圍)而不使用外部“幫助”。

您基本上可以通過執行http://whatismyipaddress.com的 WebRequest 來解析返回的頁面

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

執行此操作的最可靠方法是檢查http://checkip.dyndns.org/ 之類的站點或類似站點,因為在您實際 go 位於網絡外部之前,您找不到外部 ZA12A3079E14CED46E69BA52B1A。 但是,硬編碼這樣的 URL 要求最終失敗。 您可能希望僅在當前 IP 看起來像RFC1918私有地址(其中最熟悉的192.168.xx )時執行此檢查。

如果做不到這一點,您可以在防火牆外部實施您自己的類似服務,這樣您至少會知道它是否已損壞。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM