开发者

Best way to create IPEndpoint from string

Since IPEndpoint contains a ToString() method that outputs:

10.10.10.10:1010

There should also be Parse() and/or TryParse() meth开发者_高级运维od but there isn't.

I can split the string on the : and parse an IP address and a port.

But is there a more elegant way?


This is one solution...

public static IPEndPoint CreateIPEndPoint(string endPoint)
{
    string[] ep = endPoint.Split(':');
    if(ep.Length != 2) throw new FormatException("Invalid endpoint format");
    IPAddress ip;
    if(!IPAddress.TryParse(ep[0], out ip))
    {
        throw new FormatException("Invalid ip-adress");
    }
    int port;
    if(!int.TryParse(ep[1], NumberStyles.None, NumberFormatInfo.CurrentInfo, out port))
    {
        throw new FormatException("Invalid port");
    }
    return new IPEndPoint(ip, port);
}

Edit: Added a version that will handle IPv4 and IPv6 the previous one only handles IPv4.

// Handles IPv4 and IPv6 notation.
public static IPEndPoint CreateIPEndPoint(string endPoint)
{
    string[] ep = endPoint.Split(':');
    if (ep.Length < 2) throw new FormatException("Invalid endpoint format");
    IPAddress ip;
    if (ep.Length > 2)
    {
        if (!IPAddress.TryParse(string.Join(":", ep, 0, ep.Length - 1), out ip))
        {
            throw new FormatException("Invalid ip-adress");
        }
    }
    else
    {
        if (!IPAddress.TryParse(ep[0], out ip))
        {
            throw new FormatException("Invalid ip-adress");
        }
    }
    int port;
    if (!int.TryParse(ep[ep.Length - 1], NumberStyles.None, NumberFormatInfo.CurrentInfo, out port))
    {
        throw new FormatException("Invalid port");
    }
    return new IPEndPoint(ip, port);
}


I had the requirement of parsing an IPEndpoint with IPv6, v4 and hostnames. The solution I wrote is listed below:

    public static IPEndPoint Parse(string endpointstring)
    {
        return Parse(endpointstring, -1);
    }

    public static IPEndPoint Parse(string endpointstring, int defaultport)
    {
        if (string.IsNullOrEmpty(endpointstring)
            || endpointstring.Trim().Length == 0)
        {
            throw new ArgumentException("Endpoint descriptor may not be empty.");
        }

        if (defaultport != -1 &&
            (defaultport < IPEndPoint.MinPort
            || defaultport > IPEndPoint.MaxPort))
        {
            throw new ArgumentException(string.Format("Invalid default port '{0}'", defaultport));
        }

        string[] values = endpointstring.Split(new char[] { ':' });
        IPAddress ipaddy;
        int port = -1;

        //check if we have an IPv6 or ports
        if (values.Length <= 2) // ipv4 or hostname
        {
            if (values.Length == 1)
                //no port is specified, default
                port = defaultport;
            else
                port = getPort(values[1]);

            //try to use the address as IPv4, otherwise get hostname
            if (!IPAddress.TryParse(values[0], out ipaddy))
                ipaddy = getIPfromHost(values[0]);
        }
        else if (values.Length > 2) //ipv6
        {
            //could [a:b:c]:d
            if (values[0].StartsWith("[") && values[values.Length - 2].EndsWith("]"))
            {
                string ipaddressstring = string.Join(":", values.Take(values.Length - 1).ToArray());
                ipaddy = IPAddress.Parse(ipaddressstring);
                port = getPort(values[values.Length - 1]);
            }
            else //[a:b:c] or a:b:c
            {
                ipaddy = IPAddress.Parse(endpointstring);
                port = defaultport;
            }
        }
        else
        {
            throw new FormatException(string.Format("Invalid endpoint ipaddress '{0}'", endpointstring));
        }

        if (port == -1)
            throw new ArgumentException(string.Format("No port specified: '{0}'", endpointstring));

        return new IPEndPoint(ipaddy, port);
    }

    private static int getPort(string p)
    {
        int port;

        if (!int.TryParse(p, out port)
         || port < IPEndPoint.MinPort
         || port > IPEndPoint.MaxPort)
        {
            throw new FormatException(string.Format("Invalid end point port '{0}'", p));
        }

        return port;
    }

    private static IPAddress getIPfromHost(string p)
    {
        var hosts = Dns.GetHostAddresses(p);

        if (hosts == null || hosts.Length == 0)
            throw new ArgumentException(string.Format("Host not found: {0}", p));

        return hosts[0];
    }

This has been tested to work with the following examples:

  • 0.0.0.0:100
  • 0.0.0.0
  • [::1]:100
  • [::1]
  • ::1
  • [a:b:c:d]
  • [a:b:c:d]:100
  • example.org
  • example.org:100


It looks like there is already a built in Parse method that handles ip4 and ip6 addresses http://msdn.microsoft.com/en-us/library/system.net.ipaddress.parse%28v=vs.110%29.aspx

// serverIP can be in ip4 or ip6 format
string serverIP = "192.168.0.1";
int port = 8000;
IPEndPoint remoteEP = new IPEndPoint(IPAddress.Parse(serverIP), port);


Here is my version of parsing text to IPEndPoint:

private static IPEndPoint ParseIPEndPoint(string text)
{
    Uri uri;
    if (Uri.TryCreate(text, UriKind.Absolute, out uri))
        return new IPEndPoint(IPAddress.Parse(uri.Host), uri.Port < 0 ? 0 : uri.Port);
    if (Uri.TryCreate(String.Concat("tcp://", text), UriKind.Absolute, out uri))
        return new IPEndPoint(IPAddress.Parse(uri.Host), uri.Port < 0 ? 0 : uri.Port);
    if (Uri.TryCreate(String.Concat("tcp://", String.Concat("[", text, "]")), UriKind.Absolute, out uri))
        return new IPEndPoint(IPAddress.Parse(uri.Host), uri.Port < 0 ? 0 : uri.Port);
    throw new FormatException("Failed to parse text to IPEndPoint");
}

Tested with:

  • 0.0.0.0
  • 0.0.0.0:100
  • [::1]:100
  • [::1]:0
  • ::1
  • [2001:db8:85a3:8d3:1319:8a2e:370:7348]
  • [2001:db8:85a3:8d3:1319:8a2e:370:7348]:100
  • http://0.0.0.0
  • http://0.0.0.0:100
  • http://[::1]
  • http://[::1]:100
  • https://0.0.0.0
  • https://[::1]


Apparently, IPEndPoint.Parse and IPEndPoint.TryParse were added in .NET Core 3.0.

In case you're targeting it, give those methods a try! The implementation is seen in the link above.


Here is a very simple solution, it handles both IPv4 and IPv6.

public class IPEndPoint : System.Net.IPEndPoint
{
    public IPEndPoint(long address, int port) : base(address, port) { }
    public IPEndPoint(IPAddress address, int port) : base(address, port) { }

    public static bool TryParse(string value, out IPEndPoint result)
    {
        if (!Uri.TryCreate($"tcp://{value}", UriKind.Absolute, out Uri uri) ||
            !IPAddress.TryParse(uri.Host, out IPAddress ipAddress) ||
            uri.Port < 0 || uri.Port > 65535)
        {
            result = default(IPEndPoint);
            return false;
        }

        result = new IPEndPoint(ipAddress, uri.Port);
        return true;
    }
}

Simply use the TryParse the way you would normally.

IPEndPoint.TryParse("192.168.1.10:80", out IPEndPoint ipv4Result);
IPEndPoint.TryParse("[fd00::]:8080", out IPEndPoint ipv6Result);


This will do IPv4 and IPv6. An extension method for this functionality would be on System.string. Not sure I want this option for every string I have in the project.

private static IPEndPoint IPEndPointParse(string endpointstring)
{
    string[] values = endpointstring.Split(new char[] {':'});

    if (2 > values.Length)
    {
        throw new FormatException("Invalid endpoint format");
    }

    IPAddress ipaddress;
    string ipaddressstring = string.Join(":", values.Take(values.Length - 1).ToArray());
    if (!IPAddress.TryParse(ipaddressstring, out ipaddress))
    {
        throw new FormatException(string.Format("Invalid endpoint ipaddress '{0}'", ipaddressstring));
    }

    int port;
    if (!int.TryParse(values[values.Length - 1], out port)
     || port < IPEndPoint.MinPort
     || port > IPEndPoint.MaxPort)
    {
        throw new FormatException(string.Format("Invalid end point port '{0}'", values[values.Length - 1]));
    }

    return new IPEndPoint(ipaddress, port);
}


Create an extension method Parse and TryParse. I guess that is more elegant.


The parsing code is simple for an IPv4 endpoint, but IPEndPoint.ToString() on an IPv6 address also uses the same colon notation, but conflicts with the IPv6 address's colon notation. I was hoping Microsoft would spend the effort writing this ugly parsing code instead, but I guess I'll have to...


This is my take on the parsing of an IPEndPoint. Using the Uri class avoids having to handle the specifics of IPv4/6, and the presence or not of the port. You could can modify the default port for your application.

    public static bool TryParseEndPoint(string ipPort, out System.Net.IPEndPoint result)
    {
        result = null;

        string scheme = "iiiiiiiiiaigaig";
        GenericUriParserOptions options =
            GenericUriParserOptions.AllowEmptyAuthority |
            GenericUriParserOptions.NoQuery |
            GenericUriParserOptions.NoUserInfo |
            GenericUriParserOptions.NoFragment |
            GenericUriParserOptions.DontCompressPath |
            GenericUriParserOptions.DontConvertPathBackslashes |
            GenericUriParserOptions.DontUnescapePathDotsAndSlashes;
        UriParser.Register(new GenericUriParser(options), scheme, 1337);

        Uri parsedUri;
        if (!Uri.TryCreate(scheme + "://" + ipPort, UriKind.Absolute, out parsedUri))
            return false;
        System.Net.IPAddress parsedIP;
        if (!System.Net.IPAddress.TryParse(parsedUri.Host, out parsedIP))
            return false;

        result = new System.Net.IPEndPoint(parsedIP, parsedUri.Port);
        return true;
    }


If the port number is always provided after a ':', the following method may be a more elegant option (in code length instead of efficiency).

public static IPEndpoint ParseIPEndpoint(string ipEndPoint) {
    int ipAddressLength = ipEndPoint.LastIndexOf(':');
    return new IPEndPoint(
        IPAddress.Parse(ipEndPoint.Substring(0, ipAddressLength)),
        Convert.ToInt32(ipEndPoint.Substring(ipAddressLength + 1)));
}

It works fine for my simple application without considering complex IP address format.


A rough conversion of the .NET 3 code (for .NET 4.7) would be this:

    // ReSharper disable once InconsistentNaming
    public static class IPEndPointExtensions
    {
        public static bool TryParse(string s, out IPEndPoint result)
        {
            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))
            {
                uint port = 0;

                if (addressLength == s.Length ||
                    (uint.TryParse(s.Substring(addressLength + 1), NumberStyles.None, CultureInfo.InvariantCulture, out port) && port <= IPEndPoint.MaxPort))

                {
                    result = new IPEndPoint(address, (int)port);

                    return true;
                }
            }

            result = null;

            return false;
        }

        public static IPEndPoint Parse(string s)
        {
            if (s == null)
            {
                throw new ArgumentNullException(nameof(s));
            }

            if (TryParse(s, out IPEndPoint result))
            {
                return result;
            }

            throw new FormatException(@"An invalid IPEndPoint was specified.");
        }
    }


using System;
using System.Net;

static class Helper {
  public static IPEndPoint ToIPEndPoint(this string value, int port = IPEndPoint.MinPort) {
    if (string.IsNullOrEmpty(value) || ! IPAddress.TryParse(value, out var address)) 
      return null;
    var offset = (value = value.Replace(address.ToString(), string.Empty)).LastIndexOf(':');
    if (offset >= 0)
      if (! int.TryParse(value.Substring(offset + 1), out port) || port < IPEndPoint.MinPort || port > IPEndPoint.MaxPort)
        return null;
    return new IPEndPoint(address, port);
  }
}

class Program {
  static void Main() {
    foreach (var sample in new [] {
      // See https://docops.ca.com/ca-data-protection-15/en/implementing/platform-deployment/technical-information/ipv6-address-and-port-formats
      "192.168.0.3",
      "fe80::214:c2ff:fec8:c920",
      "10.0.1.53-10.0.1.80",
      "10.0",
      "10/7",
      "2001:0db8:85a3/48",
      "192.168.0.5:10",
      "[fe80::e828:209d:20e:c0ae]:375",
      ":137-139",
      "192.168:1024-65535",
      "[fe80::]-[fe81::]:80"
    }) {
      var point = sample.ToIPEndPoint();
      var report = point == null ? "NULL" : $@"IPEndPoint {{
  Address: {point.Address}
  AddressFamily: {point.AddressFamily}
  Port: {point.Port}
}}";
      Console.WriteLine($@"""{sample}"" to IPEndPoint is {report}
");
    }
  }
}


IPAddress ipAddress = IPAddress.Parse(yourIPAddress);
IPEndPoint localEndPoint = new IPEndPoint(ipAddress, Convert.ToInt16(yourPortAddress));
0

上一篇:

下一篇:

精彩评论

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

最新问答

问答排行榜