Random Port Generator — C# source
Generate one or many random TCP/UDP port numbers across registered, ephemeral, or the full range - optionally unique. Runs entirely in your browser with crypto-grade randomness.
This is the C# implementation — the same logic the interactive tool runs, in a shareable, citable form.
// random-port-generator — C# port: random/dynamic port picker.
//
// Display port of the CosmoDev Random Port Generator tool — same contract as
// cli/random-port-generator/random-port-generator.go (the live Go twin) and
// src/lib/random-port.ts (the canonical TypeScript implementation). The
// default rng draws 32 bits from RandomNumberGenerator (a CSPRNG) scaled
// into [0, 1) — the shape of the TS crypto.getRandomValues default; invalid
// custom ranges throw ArgumentException, mirroring the TS throw.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Security.Cryptography;
namespace RandomPortGenerator;
enum PortRange { Any, Registered, Ephemeral, Custom }
/// PortOptions in the TS lib (count/unique cover RandomPorts).
class PortOptions
{
public PortRange Range = PortRange.Registered; // the TS default (registered ports)
public int? Min; // custom-range lower bound
public int? Max; // custom-range upper bound
public int Count = 1;
public bool Unique;
public Func<double>? Rng; // injectable random in [0, 1)
}
static class RandomPort
{
static readonly Dictionary<PortRange, (int Lo, int Hi)> Ranges = new()
{
[PortRange.Any] = (1, 65_535),
[PortRange.Registered] = (1024, 49_151),
[PortRange.Ephemeral] = (49_152, 65_535),
};
/// defaultRng() in the TS lib: 32 CSPRNG bits / 2^32, like the TS
/// crypto.getRandomValues(new Uint32Array(1))[0] / 2**32.
static double DefaultRng()
{
Span<byte> buf = stackalloc byte[4];
RandomNumberGenerator.Fill(buf);
return BitConverter.ToUInt32(buf) / 4_294_967_296.0;
}
/// resolveRange() in the TS lib.
public static (int Lo, int Hi) ResolveRange(PortOptions opts) =>
opts.Range == PortRange.Custom
? (opts.Min ?? 1, opts.Max ?? 65_535)
: Ranges[opts.Range];
/// assertRange() in the TS lib — the TS Error becomes an exception.
static void AssertRange(int lo, int hi)
{
if (lo < 0 || lo > 65_535 || hi > 65_535 || lo > hi)
throw new ArgumentException($"Invalid port range {lo}-{hi}");
}
/// randomPort() in the TS lib — Port() in the Go twin.
public static int Port(PortOptions? opts = null)
{
opts ??= new PortOptions();
var rng = opts.Rng ?? DefaultRng;
var (lo, hi) = ResolveRange(opts);
AssertRange(lo, hi);
return lo + (int)Math.Floor(rng() * (hi - lo + 1));
}
/// randomPorts() in the TS lib — Ports() in the Go twin.
public static List<int> Ports(PortOptions? opts = null)
{
opts ??= new PortOptions();
var count = Math.Max(1, opts.Count);
var (lo, hi) = ResolveRange(opts);
AssertRange(lo, hi);
if (!opts.Unique)
return Enumerable.Repeat(0, count).Select(_ => Port(opts)).ToList();
// Fisher-Yates partial shuffle over the range to pick n unique ports.
var capacity = hi - lo + 1;
var n = Math.Min(count, capacity);
var rng = opts.Rng ?? DefaultRng;
var pool = new List<int>(capacity);
for (var p = lo; p <= hi; p++) pool.Add(p);
for (var i = 0; i < n; i++)
{
var j = i + (int)Math.Floor(rng() * (capacity - i));
(pool[i], pool[j]) = (pool[j], pool[i]);
}
return pool.GetRange(0, n);
}
}
public static class Program
{
public static void Main()
{
// Vectors from src/lib/random-port.test.ts — a fixed rng pins every draw.
double Zero() => 0.0;
Console.WriteLine($"registered+0 = {RandomPort.Port(new PortOptions { Rng = Zero })}"); // 1024
Console.WriteLine($"any+0 = {RandomPort.Port(new PortOptions { Range = PortRange.Any, Rng = Zero })}"); // 1
Console.WriteLine($"ephemeral+0 = {RandomPort.Port(new PortOptions { Range = PortRange.Ephemeral, Rng = Zero })}"); // 49152
Console.WriteLine(
$"custom 8000-8000 @0.9 = {RandomPort.Port(new PortOptions { Range = PortRange.Custom, Min = 8000, Max = 8000, Rng = () => 0.9 })}"); // 8000
var (lo, hi) = RandomPort.ResolveRange(new PortOptions { Range = PortRange.Custom });
Console.WriteLine($"resolveRange(custom defaults) = {lo}..{hi}"); // 1..65535
Console.WriteLine($"count=5 @0 = [{string.Join(", ", RandomPort.Ports(new PortOptions { Count = 5, Rng = Zero }))}]"); // 1024 x5
Console.WriteLine(
$"unique 3 in 1..10 @0.5 = [{string.Join(", ", RandomPort.Ports(new PortOptions { Count = 3, Unique = true, Range = PortRange.Custom, Min = 1, Max = 10, Rng = () => 0.5 }))}]"); // 6, 1, 7
Console.WriteLine(
$"unique 5 in 1..3 @0.5 = [{string.Join(", ", RandomPort.Ports(new PortOptions { Count = 5, Unique = true, Range = PortRange.Custom, Min = 1, Max = 3, Rng = () => 0.5 }))}]"); // capped at capacity 3
var p = RandomPort.Port(); // default CSPRNG, registered range
Console.WriteLine($"default rng registered = {p} (in 1024..49151)");
foreach (var bad in new[] {
new PortOptions { Range = PortRange.Custom, Min = 70000 }, // min above 65535
new PortOptions { Range = PortRange.Custom, Min = 100, Max = 50 }, // min above max
})
{
try { RandomPort.Port(bad); }
catch (ArgumentException e) { Console.WriteLine($"invalid: {e.Message}"); }
}
}
}
Also available in 13 other languages
Every CosmoDev tool ships its pure logic in TypeScript (web) and Go (CLI), with authored implementations in a dozen-plus languages — the same contract, ported. Compare all languages side by side →