Smdn.Fundamental.PortNumber version 3.0.0
smdn
released this
31 Mar 17:23
·
529 commits
to main
since this release
Released package
Release notes
The full release notes are available at gist.
Change log
Change log in this release:
- 2023-04-01 add Smdn.Fundamental.PortNumber
API changes
API changes in this release:
diff --git a/doc/api-list/Smdn.Fundamental.PortNumber/Smdn.Fundamental.PortNumber-net45.apilist.cs b/doc/api-list/Smdn.Fundamental.PortNumber/Smdn.Fundamental.PortNumber-net45.apilist.cs
new file mode 100644
index 00000000..1d0f62f5
--- /dev/null
+++ b/doc/api-list/Smdn.Fundamental.PortNumber/Smdn.Fundamental.PortNumber-net45.apilist.cs
@@ -0,0 +1,35 @@
+// Smdn.Fundamental.PortNumber.dll (Smdn.Fundamental.PortNumber-3.0.0)
+// Name: Smdn.Fundamental.PortNumber
+// AssemblyVersion: 3.0.0.0
+// InformationalVersion: 3.0.0+6f2d0f65a01b169e05b6e4adc7526ce8bbb67e78
+// TargetFramework: .NETFramework,Version=v4.5
+// Configuration: Release
+// Referenced assemblies:
+// System, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+// System.Core, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+// mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+#nullable enable annotations
+
+using System;
+using System.Collections.Generic;
+
+namespace Smdn.Net {
+ public static class PortNumberUtils {
+ public const int MaxIanaDynamicPort = 65535;
+ public const int MaxIanaSystemPort = 1023;
+ public const int MaxIanaUserPort = 49151;
+ public const int MinIanaDynamicPort = 49152;
+ public const int MinIanaSystemPort = 0;
+ public const int MinIanaUserPort = 1024;
+
+ public static TService CreateServiceWithAvailablePort<TService>(Func<int, TService> createService, Predicate<Exception> isPortInUseException) {}
+ public static TService CreateServiceWithAvailablePort<TService>(Func<int, TService> createService, Predicate<int>? exceptPort, Predicate<Exception> isPortInUseException) {}
+ public static TService CreateServiceWithAvailablePort<TService>(Func<int, TService> createService, int exceptPort, Predicate<Exception> isPortInUseException) {}
+ public static IEnumerable<int> EnumerateIanaDynamicPorts() {}
+ public static IEnumerable<int> EnumerateIanaDynamicPorts(Predicate<int>? exceptPort) {}
+ public static bool TryFindAvailablePort(Predicate<int>? exceptPort, out int port) {}
+ public static bool TryFindAvailablePort(out int port) {}
+ }
+}
+// API list generated by Smdn.Reflection.ReverseGenerating.ListApi.MSBuild.Tasks v1.2.1.0.
+// Smdn.Reflection.ReverseGenerating.ListApi.Core v1.2.0.0 (/~https://github.com/smdn/Smdn.Reflection.ReverseGenerating)
diff --git a/doc/api-list/Smdn.Fundamental.PortNumber/Smdn.Fundamental.PortNumber-net472.apilist.cs b/doc/api-list/Smdn.Fundamental.PortNumber/Smdn.Fundamental.PortNumber-net472.apilist.cs
new file mode 100644
index 00000000..6dbeff66
--- /dev/null
+++ b/doc/api-list/Smdn.Fundamental.PortNumber/Smdn.Fundamental.PortNumber-net472.apilist.cs
@@ -0,0 +1,35 @@
+// Smdn.Fundamental.PortNumber.dll (Smdn.Fundamental.PortNumber-3.0.0)
+// Name: Smdn.Fundamental.PortNumber
+// AssemblyVersion: 3.0.0.0
+// InformationalVersion: 3.0.0+6f2d0f65a01b169e05b6e4adc7526ce8bbb67e78
+// TargetFramework: .NETFramework,Version=v4.7.2
+// Configuration: Release
+// Referenced assemblies:
+// System, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+// System.Core, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+// mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+#nullable enable annotations
+
+using System;
+using System.Collections.Generic;
+
+namespace Smdn.Net {
+ public static class PortNumberUtils {
+ public const int MaxIanaDynamicPort = 65535;
+ public const int MaxIanaSystemPort = 1023;
+ public const int MaxIanaUserPort = 49151;
+ public const int MinIanaDynamicPort = 49152;
+ public const int MinIanaSystemPort = 0;
+ public const int MinIanaUserPort = 1024;
+
+ public static TService CreateServiceWithAvailablePort<TService>(Func<int, TService> createService, Predicate<Exception> isPortInUseException) {}
+ public static TService CreateServiceWithAvailablePort<TService>(Func<int, TService> createService, Predicate<int>? exceptPort, Predicate<Exception> isPortInUseException) {}
+ public static TService CreateServiceWithAvailablePort<TService>(Func<int, TService> createService, int exceptPort, Predicate<Exception> isPortInUseException) {}
+ public static IEnumerable<int> EnumerateIanaDynamicPorts() {}
+ public static IEnumerable<int> EnumerateIanaDynamicPorts(Predicate<int>? exceptPort) {}
+ public static bool TryFindAvailablePort(Predicate<int>? exceptPort, out int port) {}
+ public static bool TryFindAvailablePort(out int port) {}
+ }
+}
+// API list generated by Smdn.Reflection.ReverseGenerating.ListApi.MSBuild.Tasks v1.2.1.0.
+// Smdn.Reflection.ReverseGenerating.ListApi.Core v1.2.0.0 (/~https://github.com/smdn/Smdn.Reflection.ReverseGenerating)
diff --git a/doc/api-list/Smdn.Fundamental.PortNumber/Smdn.Fundamental.PortNumber-net6.0.apilist.cs b/doc/api-list/Smdn.Fundamental.PortNumber/Smdn.Fundamental.PortNumber-net6.0.apilist.cs
new file mode 100644
index 00000000..6326ecc4
--- /dev/null
+++ b/doc/api-list/Smdn.Fundamental.PortNumber/Smdn.Fundamental.PortNumber-net6.0.apilist.cs
@@ -0,0 +1,37 @@
+// Smdn.Fundamental.PortNumber.dll (Smdn.Fundamental.PortNumber-3.0.0)
+// Name: Smdn.Fundamental.PortNumber
+// AssemblyVersion: 3.0.0.0
+// InformationalVersion: 3.0.0+6f2d0f65a01b169e05b6e4adc7526ce8bbb67e78
+// TargetFramework: .NETCoreApp,Version=v6.0
+// Configuration: Release
+// Referenced assemblies:
+// System.Collections, Version=6.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a
+// System.Linq, Version=6.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a
+// System.Net.NetworkInformation, Version=6.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a
+// System.Net.Primitives, Version=6.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a
+// System.Runtime, Version=6.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a
+#nullable enable annotations
+
+using System;
+using System.Collections.Generic;
+
+namespace Smdn.Net {
+ public static class PortNumberUtils {
+ public const int MaxIanaDynamicPort = 65535;
+ public const int MaxIanaSystemPort = 1023;
+ public const int MaxIanaUserPort = 49151;
+ public const int MinIanaDynamicPort = 49152;
+ public const int MinIanaSystemPort = 0;
+ public const int MinIanaUserPort = 1024;
+
+ public static TService CreateServiceWithAvailablePort<TService>(Func<int, TService> createService, Predicate<Exception> isPortInUseException) {}
+ public static TService CreateServiceWithAvailablePort<TService>(Func<int, TService> createService, Predicate<int>? exceptPort, Predicate<Exception> isPortInUseException) {}
+ public static TService CreateServiceWithAvailablePort<TService>(Func<int, TService> createService, int exceptPort, Predicate<Exception> isPortInUseException) {}
+ public static IEnumerable<int> EnumerateIanaDynamicPorts() {}
+ public static IEnumerable<int> EnumerateIanaDynamicPorts(Predicate<int>? exceptPort) {}
+ public static bool TryFindAvailablePort(Predicate<int>? exceptPort, out int port) {}
+ public static bool TryFindAvailablePort(out int port) {}
+ }
+}
+// API list generated by Smdn.Reflection.ReverseGenerating.ListApi.MSBuild.Tasks v1.2.1.0.
+// Smdn.Reflection.ReverseGenerating.ListApi.Core v1.2.0.0 (/~https://github.com/smdn/Smdn.Reflection.ReverseGenerating)
diff --git a/doc/api-list/Smdn.Fundamental.PortNumber/Smdn.Fundamental.PortNumber-netstandard2.0.apilist.cs b/doc/api-list/Smdn.Fundamental.PortNumber/Smdn.Fundamental.PortNumber-netstandard2.0.apilist.cs
new file mode 100644
index 00000000..2b3be9ea
--- /dev/null
+++ b/doc/api-list/Smdn.Fundamental.PortNumber/Smdn.Fundamental.PortNumber-netstandard2.0.apilist.cs
@@ -0,0 +1,33 @@
+// Smdn.Fundamental.PortNumber.dll (Smdn.Fundamental.PortNumber-3.0.0)
+// Name: Smdn.Fundamental.PortNumber
+// AssemblyVersion: 3.0.0.0
+// InformationalVersion: 3.0.0+6f2d0f65a01b169e05b6e4adc7526ce8bbb67e78
+// TargetFramework: .NETStandard,Version=v2.0
+// Configuration: Release
+// Referenced assemblies:
+// netstandard, Version=2.0.0.0, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51
+#nullable enable annotations
+
+using System;
+using System.Collections.Generic;
+
+namespace Smdn.Net {
+ public static class PortNumberUtils {
+ public const int MaxIanaDynamicPort = 65535;
+ public const int MaxIanaSystemPort = 1023;
+ public const int MaxIanaUserPort = 49151;
+ public const int MinIanaDynamicPort = 49152;
+ public const int MinIanaSystemPort = 0;
+ public const int MinIanaUserPort = 1024;
+
+ public static TService CreateServiceWithAvailablePort<TService>(Func<int, TService> createService, Predicate<Exception> isPortInUseException) {}
+ public static TService CreateServiceWithAvailablePort<TService>(Func<int, TService> createService, Predicate<int>? exceptPort, Predicate<Exception> isPortInUseException) {}
+ public static TService CreateServiceWithAvailablePort<TService>(Func<int, TService> createService, int exceptPort, Predicate<Exception> isPortInUseException) {}
+ public static IEnumerable<int> EnumerateIanaDynamicPorts() {}
+ public static IEnumerable<int> EnumerateIanaDynamicPorts(Predicate<int>? exceptPort) {}
+ public static bool TryFindAvailablePort(Predicate<int>? exceptPort, out int port) {}
+ public static bool TryFindAvailablePort(out int port) {}
+ }
+}
+// API list generated by Smdn.Reflection.ReverseGenerating.ListApi.MSBuild.Tasks v1.2.1.0.
+// Smdn.Reflection.ReverseGenerating.ListApi.Core v1.2.0.0 (/~https://github.com/smdn/Smdn.Reflection.ReverseGenerating)
diff --git a/doc/api-list/Smdn.Fundamental.PortNumber/Smdn.Fundamental.PortNumber-netstandard2.1.apilist.cs b/doc/api-list/Smdn.Fundamental.PortNumber/Smdn.Fundamental.PortNumber-netstandard2.1.apilist.cs
new file mode 100644
index 00000000..d2b8e324
--- /dev/null
+++ b/doc/api-list/Smdn.Fundamental.PortNumber/Smdn.Fundamental.PortNumber-netstandard2.1.apilist.cs
@@ -0,0 +1,33 @@
+// Smdn.Fundamental.PortNumber.dll (Smdn.Fundamental.PortNumber-3.0.0)
+// Name: Smdn.Fundamental.PortNumber
+// AssemblyVersion: 3.0.0.0
+// InformationalVersion: 3.0.0+6f2d0f65a01b169e05b6e4adc7526ce8bbb67e78
+// TargetFramework: .NETStandard,Version=v2.1
+// Configuration: Release
+// Referenced assemblies:
+// netstandard, Version=2.1.0.0, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51
+#nullable enable annotations
+
+using System;
+using System.Collections.Generic;
+
+namespace Smdn.Net {
+ public static class PortNumberUtils {
+ public const int MaxIanaDynamicPort = 65535;
+ public const int MaxIanaSystemPort = 1023;
+ public const int MaxIanaUserPort = 49151;
+ public const int MinIanaDynamicPort = 49152;
+ public const int MinIanaSystemPort = 0;
+ public const int MinIanaUserPort = 1024;
+
+ public static TService CreateServiceWithAvailablePort<TService>(Func<int, TService> createService, Predicate<Exception> isPortInUseException) {}
+ public static TService CreateServiceWithAvailablePort<TService>(Func<int, TService> createService, Predicate<int>? exceptPort, Predicate<Exception> isPortInUseException) {}
+ public static TService CreateServiceWithAvailablePort<TService>(Func<int, TService> createService, int exceptPort, Predicate<Exception> isPortInUseException) {}
+ public static IEnumerable<int> EnumerateIanaDynamicPorts() {}
+ public static IEnumerable<int> EnumerateIanaDynamicPorts(Predicate<int>? exceptPort) {}
+ public static bool TryFindAvailablePort(Predicate<int>? exceptPort, out int port) {}
+ public static bool TryFindAvailablePort(out int port) {}
+ }
+}
+// API list generated by Smdn.Reflection.ReverseGenerating.ListApi.MSBuild.Tasks v1.2.1.0.
+// Smdn.Reflection.ReverseGenerating.ListApi.Core v1.2.0.0 (/~https://github.com/smdn/Smdn.Reflection.ReverseGenerating)
Full changes
Full changes in this release:
diff --git a/src/Smdn.Fundamental.PortNumber/Smdn.Fundamental.PortNumber.csproj b/src/Smdn.Fundamental.PortNumber/Smdn.Fundamental.PortNumber.csproj
new file mode 100644
index 00000000..e59860e1
--- /dev/null
+++ b/src/Smdn.Fundamental.PortNumber/Smdn.Fundamental.PortNumber.csproj
@@ -0,0 +1,23 @@
+<!--
+SPDX-FileCopyrightText: 2023 smdn <smdn@smdn.jp>
+SPDX-License-Identifier: MIT
+-->
+<Project Sdk="Microsoft.NET.Sdk">
+
+ <PropertyGroup>
+ <TargetFrameworks>net6.0;net472;net45;netstandard2.1;netstandard2.0</TargetFrameworks>
+ <VersionPrefix>3.0.0</VersionPrefix>
+ <VersionSuffix></VersionSuffix>
+ <!-- <PackageValidationBaselineVersion>3.0.0</PackageValidationBaselineVersion> -->
+ <Nullable>enable</Nullable>
+ </PropertyGroup>
+
+ <PropertyGroup Label="assembly attributes">
+ <CopyrightYear>2023</CopyrightYear>
+ </PropertyGroup>
+
+ <PropertyGroup Label="package properties">
+ <PackageTags>utility;port-number</PackageTags>
+ </PropertyGroup>
+
+</Project>
diff --git a/src/Smdn.Fundamental.PortNumber/Smdn.Net/PortNumberUtils.cs b/src/Smdn.Fundamental.PortNumber/Smdn.Net/PortNumberUtils.cs
new file mode 100644
index 00000000..63d7cabf
--- /dev/null
+++ b/src/Smdn.Fundamental.PortNumber/Smdn.Net/PortNumberUtils.cs
@@ -0,0 +1,155 @@
+// SPDX-FileCopyrightText: 2023 smdn <smdn@smdn.jp>
+// SPDX-License-Identifier: MIT
+#if NET472_OR_GREATER || NETSTANDARD2_1_OR_GREATER || NETCOREAPP2_0_OR_GREATER || NET5_0_OR_GREATER
+#define SYSTEM_LINQ_ENUMERABLE_TOHASHSET
+#endif
+
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Net.NetworkInformation;
+
+namespace Smdn.Net;
+
+public static class PortNumberUtils {
+ // IANA suggested range for dynamic or private ports
+ // ref: https://www.iana.org/assignments/service-names-port-numbers/service-names-port-numbers.xhtml
+ public const int MinIanaSystemPort = 0;
+ public const int MaxIanaSystemPort = 1023;
+ public const int MinIanaUserPort = 1024;
+ public const int MaxIanaUserPort = 49151;
+ public const int MinIanaDynamicPort = 49152;
+ public const int MaxIanaDynamicPort = 65535;
+
+ private static readonly Lazy<bool> isGetActiveTcpListenersAvailable = new(
+ valueFactory: () => {
+ try {
+ IPGlobalProperties.GetIPGlobalProperties().GetActiveTcpListeners();
+
+ return true;
+ }
+ catch {
+ return false;
+ }
+ },
+ isThreadSafe: true
+ );
+
+ public static TService CreateServiceWithAvailablePort<TService>(
+ Func<int, TService> createService,
+ Predicate<Exception> isPortInUseException
+ )
+ => CreateServiceWithAvailablePort(
+ createService: createService,
+ exceptPort: null,
+ isPortInUseException: isPortInUseException
+ );
+
+ public static TService CreateServiceWithAvailablePort<TService>(
+ Func<int, TService> createService,
+ int exceptPort,
+ Predicate<Exception> isPortInUseException
+ )
+ => CreateServiceWithAvailablePort(
+ createService: createService,
+ exceptPort: p => p == exceptPort,
+ isPortInUseException: isPortInUseException
+ );
+
+ public static TService CreateServiceWithAvailablePort<TService>(
+ Func<int, TService> createService,
+ Predicate<int>? exceptPort,
+ Predicate<Exception> isPortInUseException
+ )
+ {
+ if (createService is null)
+ throw new ArgumentNullException(nameof(createService));
+ if (isPortInUseException is null)
+ throw new ArgumentNullException(nameof(isPortInUseException));
+
+ if (isGetActiveTcpListenersAvailable.Value) {
+ if (TryFindAvailablePort(exceptPort, out var unusedPort)) {
+ try {
+ return createService(unusedPort);
+ }
+ catch (Exception ex) when (isPortInUseException(ex)) {
+ // ignore and continue
+ }
+ }
+ }
+
+ foreach (var dynamicPort in EnumerateIanaDynamicPorts(exceptPort)) {
+ try {
+ return createService(dynamicPort);
+ }
+ catch (Exception ex) when (isPortInUseException(ex)) {
+ continue; // ignore and continue
+ }
+ }
+
+ throw new InvalidOperationException("could not find any available port");
+ }
+
+ public static IEnumerable<int> EnumerateIanaDynamicPorts()
+ => EnumerateIanaDynamicPorts(exceptPort: null);
+
+ public static IEnumerable<int> EnumerateIanaDynamicPorts(
+ Predicate<int>? exceptPort
+ )
+ {
+ var dynamicPorts = Enumerable.Range(
+ start: MinIanaDynamicPort,
+ count: MaxIanaDynamicPort - MinIanaDynamicPort + 1
+ );
+
+ if (exceptPort is not null)
+ dynamicPorts = dynamicPorts.Where(port => !exceptPort(port));
+
+ return dynamicPorts;
+ }
+
+ // ref: https://stackoverflow.com/questions/223063/how-can-i-create-an-httplistener-class-on-a-random-port-in-c
+ public static bool TryFindAvailablePort(
+ out int port
+ )
+ => TryFindAvailablePort(
+ exceptPort: null,
+ port: out port
+ );
+
+ public static bool TryFindAvailablePort(
+ Predicate<int>? exceptPort,
+ out int port
+ )
+ {
+ port = default;
+
+ if (!isGetActiveTcpListenersAvailable.Value)
+ return false;
+
+ var activeListeners = IPGlobalProperties
+ .GetIPGlobalProperties()
+ .GetActiveTcpListeners();
+#if SYSTEM_LINQ_ENUMERABLE_TOHASHSET
+ var listeningPorts = activeListeners
+ .Select(static endPoint => endPoint.Port)
+ .ToHashSet();
+#else
+ var listeningPorts = new HashSet<int>(
+ activeListeners.Select(static endPoint => endPoint.Port)
+ );
+#endif
+
+ for (var p = MinIanaDynamicPort; p <= MaxIanaDynamicPort; p++) {
+ if (exceptPort is not null && exceptPort(p))
+ continue;
+
+ if (!listeningPorts.Contains(p)) {
+ port = p;
+ return true;
+ }
+ }
+
+ return false;
+ }
+}
Notes
Full Changelog: releases/Smdn.MSBuild.ProjectAssets.Library-1.4.7...releases/Smdn.Fundamental.PortNumber-3.0.0