获取随机免费打开的端口进行测试

Get random free opened port for tests

我正在 运行 一些与 REST API 服务的集成测试。
问题是有时硬编码端口在下次测试开始时不是空闲的。因为是之前测试打开的,还没有被系统关闭。

我用的是OWIN,应用程序在下一次测试开始的时候就关闭了。

请问有什么好的方法可以在不预先打开然后关闭的情况下确定系统空闲端口吗?或者说不可能。

因为它可能还没有被系统释放,就像它已经发生的那样。

获取免费端口

static int FreePort()
{
  TcpListener l = new TcpListener(IPAddress.Loopback, 0);
  l.Start();
  int port = ((IPEndPoint)l.LocalEndpoint).Port;
  l.Stop();
  return port;
}

作为 TempoClick 的 , we can use the IPGlobalProperties.GetActiveTcpListeners() 方法的替代方法来测试端口是否可用 - 无需尝试提前打开它。 GetActiveTcpListeners() return 系统上所有活动的 TCP 侦听器,因此我们可以使用它来确定端口是否空闲。

public bool IsFree(int port)
{
    IPGlobalProperties properties = IPGlobalProperties.GetIPGlobalProperties();
    IPEndPoint[] listeners = properties.GetActiveTcpListeners();
    int[] openPorts = listeners.Select(item => item.Port).ToArray<int>();
    return openPorts.All(openPort => openPort != port);
}

请注意 GetActiveTcpListeners() 不会 return 侦听 UDP 端点,但我们可以使用 GetActiveUdpListeners() 获取它们。

因此,您可以从默认端口(或 select 一个随机值)开始,并不断递增,直到您使用 IsFree 方法找到空闲端口。

int NextFreePort(int port = 0) 
{
    port = (port > 0) ? port : new Random().Next(1, 65535);
    while (!IsFree(port)) 
    {
        port += 1;
    }
    return port;
}

一个简单的测试:

using System;
using System.Net;
using System.Net.Sockets;
using System.Net.NetworkInformation;
using System.Linq;

class Test
{
    static void Main(string[] args)
    {
        int port = 1000;
        Console.WriteLine(IsFree(port));
        TcpListener server = new TcpListener(IPAddress.Parse("127.0.0.1"), port);
        server.Start();   
        Console.WriteLine(IsFree(port));
        Console.WriteLine(NextFreePort(port));
    }

    static bool IsFree(int port)
    {
        IPGlobalProperties properties = IPGlobalProperties.GetIPGlobalProperties();
        IPEndPoint[] listeners = properties.GetActiveTcpListeners();
        int[] openPorts = listeners.Select(item => item.Port).ToArray<int>();
        return openPorts.All(openPort => openPort != port);
    }

    static int NextFreePort(int port = 0) {
        port = (port > 0) ? port : new Random().Next(1, 65535);
        while (!IsFree(port)) {
            port += 1;
        }
        return port;
    }
}

另一种方法是使用零端口。在这种情况下,系统将从动态端口范围中 select 一个随机空闲端口。我们可以从 LocalEndpoint 属性 得到这个端口号。

TcpListener server = new TcpListener(IPAddress.Loopback, 0);
server.Start();
int port = ((IPEndPoint)server.LocalEndpoint).Port;
Console.WriteLine(port);

下面的one-liner(taken from this SO post),使用Python快速打开和关闭端口0上的套接字。当你在Python中这样做时,它会自动选择一个打开的端口,它被打印到屏幕上:

python -c 'import socket; s=socket.socket(); s.bind(("", 0)); print(s.getsockname()[1]); s.close()'