IPAddress.Parse() используя порт на IPv4
Я пытаюсь разобрать строку, содержащую IP-адрес и порт, используя IPAddress.Parse. Это хорошо работает с адресами IPv6, но не с адресами IPv4. Может ли кто-нибудь объяснить, почему это происходит?
Код, который я использую:
IPAddress.Parse("[::1]:5"); //Valid
IPAddress.Parse("127.0.0.1:5"); //null
7 ответов
Uri url;
IPAddress ip;
if (Uri.TryCreate(String.Format("http://{0}", "127.0.0.1:5"), UriKind.Absolute, out url) &&
IPAddress.TryParse(url.Host, out ip))
{
IPEndPoint endPoint = new IPEndPoint(ip, url.Port);
}
Это происходит потому, что порт не является частью IP-адреса. Он принадлежит TCP/UDP, и вам придется сначала его удалить. Класс Uri может быть полезен для этого.
IP-адрес - это не IP+ порт. Вы хотите IPEndPoint.
Пример с http://www.java2s.com/Code/CSharp/Network/ParseHostString.htm
public static void ParseHostString(string hostString, ref string hostName, ref int port)
{
hostName = hostString;
if (hostString.Contains(":"))
{
string[] hostParts = hostString.Split(':');
if (hostParts.Length == 2)
{
hostName = hostParts[0];
int.TryParse(hostParts[1], out port);
}
}
}
Изменить: Хорошо, я признаю, что это было не самое элегантное решение. Попробуйте этот, который я написал (только для вас):
// You need to include some usings:
using System.Text.RegularExpressions;
using System.Net;
// Then this code (static is not required):
private static Regex hostPortMatch = new Regex(@"^(?<ip>(?:\[[\da-fA-F:]+\])|(?:\d{1,3}\.){3}\d{1,3})(?::(?<port>\d+))?$", System.Text.RegularExpressions.RegexOptions.Compiled);
public static IPEndPoint ParseHostPort(string hostPort)
{
Match match = hostPortMatch.Match(hostPort);
if (!match.Success)
return null;
return new IPEndPoint(IPAddress.Parse(match.Groups["ip"].Value), int.Parse(match.Groups["port"].Value));
}
Обратите внимание, что этот принимает только IP-адрес, а не имя хоста. Если вы хотите поддерживать имя хоста, вам придется разрешить его в IP или не использовать IPAddress/IPEndPoint.
IPAddress.Parse предназначен для получения строки, которая содержит IP-адрес в четырехточечной нотации для IPv4 и в шестнадцатеричной системе счисления для IPv6. Таким образом, ваш первый пример работает для IPv6, а второй - не работает, потому что он не поддерживает порт для IPv4. Ссылка http://msdn.microsoft.com/en-us/library/system.net.ipaddress.parse.aspx
Как указал Тедд Хансен, вы пытаетесь проанализировать не IP-адрес, а конечную точку IP (IP-адрес + порт). А начиная с .NET Core 3.0 вы можете использовать IPEndPoint.TryParse для анализа строки как IPEndPoint:
if (IPEndPoint.TryParse("127.0.0.1:5", out IPEndPoint endpoint))
{
// parsed successfully, you can use the "endpoint" variable
}
else
{
// failed to parse
}
Если вы работаете со старыми версиями .net, вы можете использовать реализацию IPEndPoint.Parse из открытого исходного кода: https://github.com/dotnet/runtime/blob/main/src/libraries/System.Net.Primitives/src/System/ . Сеть/IPEndPoint.cs
Чтобы добавить мои два цента... Поскольку Microsoft сама реализовала TryParse
в NET Core 3.0 я решил отказаться от использования собственного парсера IP+Port и любезно позаимствовал их код с некоторыми адаптациями:
public static class IPEndPointParserExtension
{
public static bool TryParseAsIPEndPoint(this string s, out IPEndPoint result) {
#if NETCOREAPP3_0_OR_GREATER
return IPEndPoint.TryParse(s, out result);
#else
int addressLength = s.Length; // If there's no port then send the entire string to the address parser
int lastColonPos = s.LastIndexOf(':');
// Look to see if this is an IPv6 address with a port.
if (lastColonPos > 0) {
if (s[lastColonPos - 1] == ']')
addressLength = lastColonPos;
// Look to see if this is IPv4 with a port (IPv6 will have another colon)
else if (s.Substring(0, lastColonPos).LastIndexOf(':') == -1)
addressLength = lastColonPos;
}
if (IPAddress.TryParse(s.Substring(0, addressLength), out IPAddress address)) {
long port = 0;
if (addressLength == s.Length ||
(long.TryParse(s.Substring(addressLength + 1), out port)
&& port <= IPEndPoint.MaxPort)) {
result = new IPEndPoint(address, (int)port);
return true;
}
}
result = null;
return false;
#endif
}
public static IPEndPoint AsIPEndPoint(this string s) =>
s.TryParseAsIPEndPoint(out var endpoint)
? endpoint
: throw new FormatException($"'{s}' is not a valid IP Endpoint");
}
Мои изменения заключались в основном в обмене
Span<char>
за
string
и сделать его методом расширения класса
String
сам. Я также условно компилировал для использования реализации Microsoft, если она доступна (NET Core 3.0 или выше).
Следующие тесты nUnit показывают, как использовать код:
[Test]
public void CanParseIpv4WithPort() {
var sIp = "192.168.0.233:8080";
if (sIp.TryParseAsIPEndPoint(out var endpoint)) {
var expected = new IPEndPoint(new IPAddress(new byte[] { 192, 168, 0, 233 }), 8080);
Assert.AreEqual(expected, endpoint);
} else
Assert.Fail($"Failed to parse {sIp}");
}
[Test]
public void CanParseIpv6WithPort() {
var sIp = "[2001:db8:85a3:8d3:1319:8a2e:370:7348]:443";
if (sIp.TryParseAsIPEndPoint(out var endpoint)) {
var expected = new IPEndPoint(IPAddress.Parse("2001:db8:85a3:8d3:1319:8a2e:370:7348"), 443);
Assert.AreEqual(expected, endpoint);
} else
Assert.Fail($"Failed to parse {sIp}");
}
Вы также можете использовать
AsIpEndPoint
который вызовет исключение, если ему не удастся проанализировать IP-адрес и порт (порт не является обязательным):
var ep = "127.0.0.1:9000".AsIPEndPoint();