Получить внешний IP-адрес через удаленное взаимодействие в C # - PullRequest
19 голосов
/ 16 сентября 2008

Мне нужно выяснить внешний IP-адрес компьютера, на котором запущено приложение C #.

В приложении у меня есть подключение (через .NET Remoting) к серверу. Есть ли хороший способ получить адрес клиента на стороне сервера?

(Я отредактировал вопрос, чтобы быть немного более ясным. Я извиняюсь перед всеми добрыми людьми, которые приложили все усилия, чтобы ответить на вопрос, когда я, возможно, был немного слишком расплывчатым)

Решение:
Я нашел способ, который работал отлично для меня. Благодаря реализации пользовательских IServerChannelSinkProvider и IServerChannelSink, где у меня есть доступ к CommonTransportKeys.IPAddress, легко добавить клиентский ip в 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;
}

А потом (когда я получаю запрос от клиента) просто получите IP-адрес из CallContext следующим образом.

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 обратно клиенту.

Ответы [ 12 ]

19 голосов
/ 16 сентября 2008

Это один из тех вопросов, где вы должны посмотреть глубже и, возможно, переосмыслить исходную проблему; в этом случае «Зачем вам нужен внешний IP-адрес?»

Проблема в том, что у компьютера может не быть внешнего IP-адреса. Например, мой ноутбук имеет внутренний IP-адрес (192.168.x.y), назначенный маршрутизатором. Сам маршрутизатор имеет внутренний IP-адрес, но его «внешний» IP-адрес также является внутренним. Он используется только для связи с модемом DSL, который фактически имеет внешний IP-адрес, выходящий в Интернет.

Таким образом, возникает реальный вопрос: «Как я могу получить IP-адрес устройства, выходящего в Интернет, через 2 прыжка?» И ответ, как правило, нет; по крайней мере, не используя такой сервис, как whatismyip.com, который вы уже отклонили, или совершив по-настоящему масштабный хак, включив жесткое кодирование пароля модема DSL в ваше приложение, запрос модема DSL и очистку экрана страницы администратора (и Бог поможет вам если модем когда-либо будет заменен).

РЕДАКТИРОВАТЬ: Теперь, чтобы применить это к измененному вопросу: «Как мне получить IP-адрес моего клиента от серверного .NET-компонента?» Как и whatismyip.com, лучшее, что сервер сможет сделать, это дать вам IP-адрес вашего устройства, подключенного к Интернету, который вряд ли будет фактическим IP-адресом компьютера, на котором запущено приложение. Возвращаясь к моему ноутбуку, если мой IP-адрес в Интернете был 75.75.75.75, а IP-адрес локальной сети был 192.168.0.112, сервер мог видеть только 75.75.75.75 IP-адрес. Это дойдет до моего модема DSL. Если ваш сервер хочет установить отдельное соединение обратно с моим ноутбуком, мне сначала нужно настроить DSL-модем и любые маршрутизаторы между ним и моим ноутбуком, чтобы они распознавали входящие соединения с вашего сервера и соответствующим образом маршрутизировали их. Есть несколько способов сделать это, но это выходит за рамки этой темы.

Если вы на самом деле пытаетесь установить соединение с сервера обратно к клиенту, переосмыслите свой дизайн, потому что вы углубляетесь в территорию WTF (или, по крайней мере, делаете приложение более сложным для развертывания).

6 голосов
/ 16 сентября 2008

Dns.GetHostEntry (Dns.GetHostName ()); вернет массив IP-адресов. Первым должен быть внешний IP, остальные будут теми, что стоят за NAT.

Итак:

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

EDIT:

Есть сообщения, что это не работает для некоторых людей. Это для меня, но, возможно, в зависимости от конфигурации вашей сети, это может не сработать.

5 голосов
/ 16 сентября 2008

Лучше просто использовать http://www.whatismyip.com/automation/n09230945.asp, он выводит IP только для автоматических поисков.

Если вы хотите что-то, что не зависит от кого-то, поднимите свою собственную страницу http://www.unkwndesign.com/ip.php это просто быстрый скрипт:

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

Единственным недостатком здесь является то, что он будет извлекать только внешний IP-адрес интерфейса, который использовался для создания запроса.

4 голосов
/ 24 августа 2009

Я нашел способ, который отлично сработал для меня. Реализуя пользовательские IServerChannelSinkProvider и IServerChannelSink, где у меня есть доступ к CommonTransportKeys.IPAddress, легко добавить ip клиента в 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;
}

А потом (когда я получаю запрос от клиента) просто получите IP-адрес из CallContext следующим образом.

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 обратно клиенту.

4 голосов
/ 16 сентября 2008

Ответ Джонатана Холланда в принципе верен, но стоит добавить, что вызовы API, стоящие за Dns.GetHostByName, отнимают много времени, и рекомендуется кэшировать результаты, чтобы код вызывался только один раз.

3 голосов
/ 16 сентября 2008

Основная проблема заключается в том, что публичный IP-адрес не обязательно соотносится с локальным компьютером, на котором запущено приложение. Он переводится из внутренней сети через брандмауэр. Чтобы действительно получить общедоступный IP-адрес без опроса локальной сети, нужно сделать запрос на интернет-странице и вернуть результат. Если вы не хотите использовать общедоступный сайт типа WhatIsMyIP.com, вы можете легко создать его и разместить его самостоятельно - предпочтительно в виде веб-службы, чтобы вы могли сделать простой вызов, совместимый с мылом, из своего приложения. Вам не обязательно делать снимок экрана так же, как закулисный пост и читать ответ.

2 голосов
/ 30 мая 2009

Решение Патрика у меня работает!

Я сделал одно важное изменение. В сообщении процесса я установил CallContext, используя этот код:

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

Это помещает IP-адрес в правильный CallContext, так что его можно позже получить с помощью GetClientIP().

2 голосов
/ 16 сентября 2008

Если вам нужен только IP-адрес, связанный с адаптером, вы можете использовать WMI и класс Win32_NetworkAdapterConfiguration.

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

1 голос
/ 16 сентября 2008

Итак, если у вас есть 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);
}

Удачи.

0 голосов
/ 16 сентября 2008

Я считаю, что теоретически вы не можете сделать это, находясь за маршрутизатором (например, используя недопустимые диапазоны IP-адресов), не прибегая к внешней «помощи».

...