Есть ли стандартный .NET-декодер для адресов ipv4 и ipv6?

Я хотел бы написать довольно простое сетевое клиент-серверное приложение. Я использую только чистую сеть IPv4, но было бы неплохо, чтобы мой код соответствовал требованиям завтрашнего дня. Я, вероятно, буду использовать TcpListener / TcpClient, потому что предварительное исследование WCF показывает, что он слишком сложен в настройке и труден для понимания.

Предоставляет ли .NET для клиентской стороны возможность автоматического декодирования строки, содержащей адрес IPv4 или IPv6 (где адрес IPv4 включает номер порта)? Бонусные баллы, если он может разрешить доменное имя.

Что касается серверной стороны, я слышал, что IPv6 не использует номера портов, поэтому какой эквивалент порта для прослушивания и существует ли стандартный способ отличить строку номера порта IPv4 от эквивалента IPv6? Неважно, сервисы IPv6 имеют 16-битные номера портов, как и IPv4.


person Qwertie    schedule 11.03.2011    source источник


Ответы (4)


Да, System.Net.IPAddress

IPAddress.Parse( "fe80::21c:42ff:fe00:8%vnic0" );
IPAddress.Parse( "127.0.0.1" );

И протестировать на IPv4 или v6

if( IPAddress.Parse(...).AddressFamily == AddressFamily.InterNetwork )
  // IPv4 address
person Paul Alexander    schedule 11.03.2011
comment
IPAddress.Parse (23.45.67.89:1024) выбрасывает FormatException, поэтому я предполагаю, что ответ вроде как. - person Qwertie; 11.03.2011
comment
Это недействительный IP-адрес. Номера портов не являются частью IP-части стека TCP / IP. У IP-адреса нет порта. То, что вы используете, ближе к URI, который также можно анализировать с помощью класса Uri. - person Paul Alexander; 11.03.2011
comment
Поскольку адреса IPv6 также содержат двоеточия, мне интересно, как правильно декодировать адрес IPv6 или адрес IPv4, содержащий номер порта. Может быть, начать с IPAddress.TryParse, и если это не поможет, найдите номер порта, проанализируйте его, удалите и повторите попытку. И если это все еще не удается, используйте System.Net.Dns.Resolve для разрешения в качестве имени домена - но я очень озадачен, потому что он возвращает IPHostEntry, который может содержать несколько IP-адресов. С каких это пор одно доменное имя имеет несколько IP-адресов? Мы должны попытаться подключиться ко всем из них? - person Qwertie; 11.03.2011
comment
IP-адреса не содержат номер порта - ни в версии 4, ни в версии 6. Вы пытаетесь разобрать URI, который использует IP-адрес в качестве имени хоста. Просто используйте для этого Uri. - person Paul Alexander; 11.03.2011

System.Net.IPAddress может использоваться для анализа строк, представляющих действительные адреса IPv4 и IPv6. System.Net.Dns может использоваться для разрешения имен хостов и адресов в сети.

Для обоих,

using System.Net;
person darth10    schedule 11.03.2011
comment
Вы не собираетесь упоминать, какие методы вызывать? - person Qwertie; 11.03.2011
comment
IPAddress.Parse () (или IPAddress.TryParse ()) и Dns.GetHostEntry () - person darth10; 31.03.2011

@Qwertie сказал:

Поскольку адреса IPv6 также содержат двоеточия, мне интересно, как правильно декодировать адрес IPv6 или адрес IPv4, содержащий номер порта.

Как отмечали другие, номер порта не является частью IP-адреса (IPv4 или IPv6). IP-адреса являются атомарными целыми числами без знака (IPv4 - 32-битный, IPv6 - 128-битный). Когда они представлены в символьной форме (например, URI), они могут быть объединены с номером порта (и другой информацией). В URI хост и порт [могут быть] частью полномочий части URI.

URI можно разбить на составные части с помощью System.Uri. Часть Authority URI состоит из следующих свойств: Host, Port (и, необязательно, и не рекомендуется, подкомпонент UserInfo, состоящий из имени пользователя и пароля). Свойство HostNameType сообщит вам, какое у вас имя хоста, значение перечисления UriHostNameType: Dns, Ipv4, Ipv6 или что-то еще.

Вы также можете разобрать URI на его составные части, используя регулярное выражение, определенное в Приложении B к RFC 3986:

Appendix B.  Parsing a URI Reference with a Regular Expression
As the "first-match-wins" algorithm is identical to the "greedy" disambiguation method used by POSIX regular expressions, it is natural and commonplace to use a regular expression for parsing the potential five components of a URI reference.
The following line is the regular expression for breaking-down a well-formed URI reference into its components.
^(([^:/?#]+):)?(//([^/?#]))?([^?#])(\?([^#]))?(#(.))? 12 3 4 5 6 7 8 9
The numbers in the second line above are only to assist readability; they indicate the reference points for each subexpression (i.e., each paired parenthesis). We refer to the value matched for subexpression <n> as $<n>. For example, matching the above expression to
http://www.ics.uci.edu/pub/ietf/uri/#Related
results in the following subexpression matches:
$1 = http: $2 = http $3 = //www.ics.uci.edu $4 = www.ics.uci.edu $5 = /pub/ietf/uri/ $6 = $7 = $8 = #Related $9 = Related
where <undefined> indicates that the component is not present, as is the case for the query component in the above example. Therefore, we can determine the value of the five components as
scheme = $2 authority = $4 path = $5 query = $7 fragment = $9
Going in the opposite direction, we can recreate a URI reference from its components by using the algorithm of Section 5.3.

Однако обратите внимание, что это регулярное выражение кажется немного неправильным. Согласно исправлению для RFC 3986 и предшествующего ему RFC 2396:

Errata ID: 1933
Status: Verified Type: Technical Reported By: Skip Geel Date Reported: 2009-10-25 Verifier Name: Peter Saint-Andre Date Verified: 2010-11-11
Section appendix B says:
^(([^:/?#]+):)?(//([^/?#]))?([^?#])(\?([^#]))?(#(.))?
It should say:
/^(([^:\/?#]+):)?(\/\/([^\/?#]))?([^?#])(\?([^#]))?(#(.))?/
Notes:
A Regular Expression is delimited by the slash ("/"). Within it a slash should be preceded by a back-slash ("\").
Peter: This also applies to RFC 3986.

person Nicholas Carey    schedule 11.03.2011
comment
Исходное регулярное выражение отлично подходит для .NET. Единственная разница заключается в том, что вторая версия разделена косой чертой и, следовательно, требует дополнительных обратных косых черт. - person Qwertie; 11.03.2011

Как упоминал Пол, простой IP-адрес без номера порта может быть проанализирован IPAddress.Parse(). Однако при наличии номера порта и / или имени хоста (12.34.56.78:90 или www.example.com:5555) необходим другой подход. Если вы хотите использовать TcpClient для подключения, эта функция сделает это:

public static TcpClient Connect(string ipAndPort, int defaultPort)
{
    if (ipAndPort.Contains("/"))
        throw new FormatException("Input should only be an IP address and port");

    // Uri requires a prefix such as "http://", "ftp://" or even "foo://".
    // Oddly, Uri accepts the prefix "//" UNLESS there is a port number.
    Uri uri = new Uri("tcp://" + ipAndPort);

    string ipOrDomain = uri.Host;
    int port = uri.Port != -1 ? uri.Port : defaultPort;
    return new TcpClient(ipOrDomain, port);
}

Параметр defaultPort указывает порт, который следует использовать, если во входной строке его нет. Например:

using (NetworkStream s = Connect("google.com", 80).GetStream())
{
    byte[] line = Encoding.UTF8.GetBytes("GET / HTTP/1.0\r\n\r\n");
    s.Write(line, 0, line.Length);

    int b;
    while ((b = s.ReadByte()) != -1)
        Console.Write((char)b);
}

Чтобы декодировать адрес без подключения к нему (например, чтобы убедиться, что он действителен или потому что вы подключаетесь через API, для которого требуется IP-адрес), этот метод сделает это (и при необходимости выполнит поиск DNS):

public static IPAddress Resolve(string ipAndPort, ref int port, bool resolveDns)
{
    if (ipAndPort.Contains("/"))
        throw new FormatException("Input address should only contain an IP address and port");

    Uri uri = new Uri("tcp://" + ipAndPort);

    if (uri.Port != -1)
        port = uri.Port;
    if (uri.HostNameType == UriHostNameType.IPv4 || uri.HostNameType == UriHostNameType.IPv6)
        return IPAddress.Parse(uri.Host);
    else if (resolveDns)
        return Dns.GetHostAddresses(uri.Host)[0];
    else
        return null;
}

Любопытно, что Dns.GetHostAddresses может возвращать несколько адресов. Я спросил об этом, и очевидно, можно просто взять первый адрес.

Исключение будет вызвано, если есть синтаксическая ошибка или проблема с разрешением доменного имени (FormatException или SocketException). Если пользователь указал доменное имя, но resolveDns==false, этот метод возвращает null.

person Qwertie    schedule 11.03.2011