开发者

IPAddress.Parse() using port on IPv4

I'm trying to parse a s开发者_JS百科tring containing an IP address and a port using IPAddress.Parse. This works well with IPv6 addresses but not with IPv4 addresses. Can somone explain why this happens?

The code I'm using is:

IPAddress.Parse("[::1]:5"); //Valid
IPAddress.Parse("127.0.0.1:5"); //null


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);
}


This happens because the port is not part of the IP address. It belongs to TCP/UDP, and you'll have to strip it out first. The Uri class might be helpful for this.


IPAddress is not IP+Port. You want IPEndPoint.

Example from 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);
      }
   }
}

Edit: Ok, I'll admit that wasn't the most elegant solution. Try this one I wrote (just for you) instead:

// 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));
}

Note that this one ONLY accepts IP address, not hostname. If you want to support hostname you'll either have to resolve it to IP or not use IPAddress/IPEndPoint.


IPAddress.Parse is meant to take A string that contains an IP address in dotted-quad notation for IPv4 and in colon-hexadecimal notation for IPv6. So your first example works for IPv6 and your second example fails because it doesnt support a port for IPv4. Link http://msdn.microsoft.com/en-us/library/system.net.ipaddress.parse.aspx


As Tedd Hansen pointed out, what you are trying to parse is not an IP address but an IP endpoint (IP address + port). And since .NET Core 3.0, you can use IPEndPoint.TryParse to parse a string as an IPEndPoint:

if (IPEndPoint.TryParse("127.0.0.1:5", out IPEndPoint endpoint))
{
    // parsed successfully, you can use the "endpoint" variable
    Console.WriteLine(endpoint.Address.ToString()); // writes "127.0.0.1"
    Console.WriteLine(endpoint.Port.ToString()); // writes "5"
}
else
{
    // failed to parse
}


If you work on older versions of .net you can take IPEndPoint.Parse implementation from open source: https://github.com/dotnet/runtime/blob/main/src/libraries/System.Net.Primitives/src/System/Net/IPEndPoint.cs


To add my two cents... Since Microsoft itself implemented TryParse in NET Core 3.0 I've opted to stop using my custom IP+Port parser and kindly borrowed their code with some adaptations:

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");
   
}

My changes were to basically exchange Span<char> for string and make it an extension method of the class String itself. I've also conditionally compile to use Microsoft's implementation if it is available (NET Core 3.0 or greater).

The following nUnit tests show how to use the code:

[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}");
}

You can also use AsIpEndPoint which will throw an exception if it fails to parse the IP address and port (port is optional):

var ep = "127.0.0.1:9000".AsIPEndPoint();
0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜