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をクライアントに送り返すことができます。
私は自分にとってうまくいく方法を見つけました。 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をクライアントに送り返すことができます。
これは、より深く調べて、元の問題を再考する必要がある質問の1つです。この場合、「なぜ外部IPアドレスが必要なのですか?」
問題は、コンピューターに外部IPアドレスがない可能性があることです。たとえば、私のラップトップには、ルーターによって割り当てられた内部IPアドレス(192.168.x.y)があります。ルータ自体には内部IPアドレスがありますが、その「外部」IPアドレスも内部にあります。これは、実際にはインターネットに直接接続された外部IPアドレスを持つDSLモデムとの通信にのみ使用されます。
したがって、本当の問題は、「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モデムと、サーバーからの着信接続を認識して適切にルーティングするように、DSLモデムとそれとラップトップの間のルーターを構成する必要があります。これを行うにはいくつかの方法がありますが、このトピックの範囲外です。
実際にサーバーからクライアントへの接続を確立しようとしている場合は、WTFの領域を掘り下げているため、設計を再考してください(または、少なくとも、アプリケーションの展開がはるかに困難になります)。
Dns.GetHostEntry(Dns.GetHostName()); IPアドレスの配列を返します。最初のものは外部IPである必要があり、残りはNATの背後にあるものになります。
そう:
IPHostEntry IPHost = Dns.GetHostEntry(Dns.GetHostName());
string externalIP = IPHost.AddressList[0].ToString();
編集:
これが一部の人にとってはうまくいかないという報告があります。私にとっては問題ありませんが、ネットワーク構成によっては機能しない場合があります。
使用する方が良い 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呼び出しはかなり時間がかかるため、コードを1回だけ呼び出す必要があるように、結果をキャッシュすることをお勧めします。
主な問題は、パブリックIPアドレスが、アプリケーションを実行しているローカルコンピューターと必ずしも相関していないことです。これは、ファイアウォールを介して内部ネットワークから変換されます。ローカルネットワークに問い合わせることなくパブリックIPを真に取得するには、インターネットページにリクエストを送信し、結果を返す必要があります。公開されているWhatIsMyIP.comタイプのサイトを使用したくない場合は、簡単に作成して自分でホストできます。できればWebサービスとして、アプリケーション内から簡単に石鹸に準拠した呼び出しを行うことができます。舞台裏の投稿や応答の読み取りほど、スクリーンキャプチャを行う必要はありません。
パトリックの解決策は私のために働きます!
作った 1 重要な変更。処理中のメッセージで、次のコードを使用してCallContext
を設定しました。
_// try to set the call context
LogicalCallContext lcc = (LogicalCallContext)requestMessage.Properties["__CallContext"];
if (lcc != null)
{
lcc.SetData("ClientIP", ipAddr);
}
_
これにより、IPアドレスが正しいCallContextに配置されるため、後でGetClientIP()
を使用して取得できます。
アダプターにバインドされているIPだけが必要な場合は、WMIとWin32_NetworkAdapterConfigurationクラスを使用できます。
http://msdn.Microsoft.com/en-us/library/aa394217(VS.85).aspx
さて、クライアントにSystem.Net.Sockets.TcpClient
が接続されていると仮定すると、(サーバー上で)client.Client.RemoteEndPoint
を使用できます。これにより、クライアントを指すSystem.Net.EndPoint
が得られます。そのshouldSystem.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範囲を使用しているとき)にそのようなことを行うことはできないと思います。