Skip to content

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 from std::random_device — the system entropy source —
// through std::uniform_real_distribution (display-grade randomness like the
// other C-family ports). An injectable rng keeps generation deterministic
// (the TS test surface); invalid custom ranges throw std::invalid_argument,
// mirroring the TS throw.
#include <algorithm>
#include <cmath>
#include <functional>
#include <iostream>
#include <map>
#include <random>
#include <stdexcept>
#include <string>
#include <utility>
#include <vector>

enum class PortRange { Any, Registered, Ephemeral, Custom };

/// PortOptions in the TS lib (count/unique cover randomPorts). min/max of 0
/// mean unset, falling back to the full-range defaults like
/// resolveRange({range:'custom'}) in the TS lib.
struct PortOptions {
    PortRange range = PortRange::Registered; // the TS default (registered ports)
    int min = 0;         // custom-range lower bound
    int max = 0;         // custom-range upper bound
    int count = 1;       // how many ports (randomPorts)
    bool unique = false; // dedupe (randomPorts)
    std::function<double()> rng; // injectable random in [0, 1); empty = default
};

namespace {

const std::map<PortRange, std::pair<int, int>> kRanges = {
    {PortRange::Any, {1, 65535}},
    {PortRange::Registered, {1024, 49151}},
    {PortRange::Ephemeral, {49152, 65535}},
};

/// defaultRng() in the TS lib: one draw in [0, 1) from the system entropy source.
double defaultRng() {
    static std::mt19937_64 engine{std::random_device{}()};
    return std::uniform_real_distribution<double>(0.0, 1.0)(engine);
}

} // namespace

/// resolveRange() in the TS lib.
std::pair<int, int> resolveRange(const PortOptions& opts) {
    if (opts.range == PortRange::Custom) {
        return {opts.min ? opts.min : 1, opts.max ? opts.max : 65535};
    }
    return kRanges.at(opts.range);
}

/// assertRange() in the TS lib — the TS Error becomes an exception.
void assertRange(int lo, int hi) {
    if (lo < 0 || lo > 65535 || hi > 65535 || lo > hi) {
        throw std::invalid_argument("Invalid port range " + std::to_string(lo) + "-" + std::to_string(hi));
    }
}

/// randomPort() in the TS lib — Port() in the Go twin.
int randomPort(const PortOptions& opts) {
    auto rng = opts.rng ? opts.rng : defaultRng;
    auto [lo, hi] = resolveRange(opts);
    assertRange(lo, hi);
    return lo + static_cast<int>(std::floor(rng() * (hi - lo + 1)));
}

/// randomPorts() in the TS lib — Ports() in the Go twin.
std::vector<int> randomPorts(const PortOptions& opts) {
    const int count = std::max(1, opts.count);
    auto [lo, hi] = resolveRange(opts);
    assertRange(lo, hi);
    if (!opts.unique) {
        std::vector<int> out;
        out.reserve(count);
        for (int i = 0; i < count; i++) out.push_back(randomPort(opts));
        return out;
    }

    // Fisher-Yates partial shuffle over the range to pick n unique ports.
    const int capacity = hi - lo + 1;
    const int n = std::min(count, capacity);
    auto rng = opts.rng ? opts.rng : defaultRng;
    std::vector<int> pool(capacity);
    for (int i = 0; i < capacity; i++) pool[i] = lo + i;
    for (int i = 0; i < n; i++) {
        int j = i + static_cast<int>(std::floor(rng() * (capacity - i)));
        std::swap(pool[i], pool[j]);
    }
    pool.resize(n);
    return pool;
}

int main() {
    // Vectors from src/lib/random-port.test.ts — a fixed rng pins every draw.
    auto zero = [] { return 0.0; };
    std::cout << "registered+0 = " << randomPort({.rng = zero}) << '\n'; // 1024
    std::cout << "any+0 = " << randomPort({.range = PortRange::Any, .rng = zero}) << '\n'; // 1
    std::cout << "ephemeral+0 = " << randomPort({.range = PortRange::Ephemeral, .rng = zero}) << '\n'; // 49152
    std::cout << "custom 8000-8000 @0.9 = "
              << randomPort({.range = PortRange::Custom, .min = 8000, .max = 8000, .rng = [] { return 0.9; }})
              << '\n'; // 8000
    auto d = resolveRange({.range = PortRange::Custom});
    std::cout << "resolveRange(custom defaults) = " << d.first << ".." << d.second << '\n'; // 1..65535

    for (const auto& [label, ports] : std::vector<std::pair<const char*, std::vector<int>>>{
             {"count=5 @0", randomPorts({.count = 5, .rng = zero})}, // [1024 x5]
             {"unique 3 in 1..10 @0.5",
              randomPorts({.count = 3, .unique = true, .range = PortRange::Custom,
                           .min = 1, .max = 10, .rng = [] { return 0.5; }})}, // [6, 1, 7]
             {"unique 5 in 1..3 @0.5",
              randomPorts({.count = 5, .unique = true, .range = PortRange::Custom,
                           .min = 1, .max = 3, .rng = [] { return 0.5; }})}, // capped at capacity 3
         }) {
        std::cout << label << " = [";
        for (size_t i = 0; i < ports.size(); i++) std::cout << (i ? ", " : "") << ports[i];
        std::cout << "]\n";
    }

    int p = randomPort({}); // default rng, registered range
    std::cout << "default rng registered = " << p << " (in 1024..49151)\n";
    for (const auto& bad : std::vector<PortOptions>{
             {.range = PortRange::Custom, .min = 70000}, // min above 65535
             {.range = PortRange::Custom, .min = 100, .max = 50}, // min above max
         }) {
        try {
            randomPort(bad);
        } catch (const std::invalid_argument& e) {
            std::cout << "invalid: " << e.what() << '\n';
        }
    }
    return 0;
}

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 →