动态取得本机可用的端口

今天在项目中考虑这样一件事情:我需要动态实例化一个服务,监听某个端口。那么该怎么来实现这个需求呢?

我立马想到是否有这样的函数,例如GetAvaliablePorts呢?主意不错,但确没有找到。原先Win32 API中有一个函数(EnumPorts),但import来过来之后也没有用。

此路不通,看来要自己动手了。再大的困难也吓不倒英雄的中华儿女嘛。

首先,要知道一些有关端口号的基础知识

  • 所有的端口都应该大于0,而且小于65535
  • 微软建议,1024及以前的端口号保留给系统用。也就是说,我们自己程序监听的端口最好是大于1024

其次,因为没有内置的方法来测试某个端口是否可用,我们可能需要自己编写方法来做这个事情。那么怎么做呢?我想到,可以用socket来测试。我编写了如下这样一个函数

/// <summary>
/// 这个方法是验证某个端口是否可用
/// </summary>
/// <param name="port"></param>
/// <returns></returns>
static bool IsAvaliable(int port)
{

    Socket sk = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.IP);
    bool result = false;
    try
    {
        sk.Bind(new IPEndPoint(IPAddress.Any, port));//尝试绑定,因为如果该端口已经被使用,则无法绑定,会导致异常
        result = true;
    }
    catch
    {
        result = false;
    }
    finally
    {
        sk.Close();
    }

    return result;
}

然后,我就可以在代码中这样使用

static void Main(string[] args)
{

    Console.WriteLine(IsAvaliable(8080));//如果8080端口可用,则返回true,反之,返回false
    Console.Read();
}

 

最后,我还可以编写另外一个方法,得到一个可用的端口

/// <summary>
/// 这个方法取得一个可用的端口
/// </summary>
/// <returns></returns>
static int GetAvaliablePort()
{
    Random rnd = new Random();
    int port = rnd.Next(1024, 65535);//随机产生一个动态的端口号
    while (!IsAvaliable(port))
    {
        port = rnd.Next(1024, 65535);
    }

    return port;
}

在调用程序中,就可以这样来调用它

Console.WriteLine(GetAvaliablePort());

看起来不错,对吧?但是如果我们要一次性得到10个不同的可用端口号呢?我们会大致下面这样写代码

for (int i = 0; i < 10; i++)
{
    Console.WriteLine(GetAvaliablePort());
}

但是,让人吃惊的是,我们看到下面这样的输出结果。

image

这是如何解释呢?为什么既然是随机数,又仍然是重复呢?

这是因为我们的10次循环之间几乎没有间隔(特别是我的机器配置又很好的情况下),所以,对于随机数的产生器,它实际上无法进行区分。我自己感觉就是,如果两个操作之间几乎没有时间间隔,那么随机数也是一样的。

那么该如何解决该问题?好吧,我们可以刻意地让每次循环有一些间隔

for (int i = 0; i < 10; i++)
{
    Thread.Sleep(100);//我们强制让进程休眠0.1秒
    Console.WriteLine(GetAvaliablePort());
}

image

这样大家总该是没有意见了吧?

最后,我们对代码进行一些重构,封装之后的完整代码如下

using System;
using System.Collections.Generic;
using System.Text;
using System.Runtime.InteropServices;

using System.Net.Sockets;
using System.Net;

using System.Threading;

namespace ConsoleApplication1
{
    class Program
    {
        static void Main(string[] args)
        {
            PortHelper helper = new PortHelper();
            Console.WriteLine("当前8080端口是否可用?"+helper.IsAvaliable(8080));

            Console.WriteLine("取得10个可用的端口:");

            for (int i = 0; i < 10; i++)
            {
                Thread.Sleep(100);
                Console.WriteLine(helper.GetAvaliablePort());
            }

            Console.Read();
        }

    }

        public class PortHelper
        {

            /// <summary>
            /// 这个方法是验证某个端口是否可用
            /// </summary>
            /// <param name="port"></param>
            /// <returns></returns>
            public bool IsAvaliable(int port)
            {

                Socket sk = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.IP);
                bool result = false;
                try
                {
                    sk.Bind(new IPEndPoint(IPAddress.Any, port));//尝试绑定,因为如果该端口已经被使用,则无法绑定,会导致异常
                    result = true;
                }
                catch
                {
                    result = false;
                }
                finally
                {
                    sk.Close();
                }

                return result;

            }

            /// <summary>
            /// 这个方法取得一个可用的端口
            /// </summary>
            /// <returns></returns>
            public int GetAvaliablePort()
            {
                Random rnd = new Random();
                int port = rnd.Next(1024, 65535);//随机产生一个动态的端口号
                while (!IsAvaliable(port))
                {
                    port = rnd.Next(1024, 65535);
                }

                return port;
            }

        }
}

image

原文地址:https://www.cnblogs.com/chenxizhang/p/1493139.html