zoukankan      html  css  js  c++  java
  • 动态取得本机可用的端口

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

    我立马想到是否有这样的函数,例如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

  • 相关阅读:
    eclips断点调试
    单位换算
    信息论与编码复习
    嵌入式学习笔记
    DAVINCI项目日志
    英语考试方法
    虚拟机安装Ubuntu的上网设置(有线网络和无线网络)
    重装系统必须备份的几种数据
    linux笔记
    word应用技巧
  • 原文地址:https://www.cnblogs.com/chenxizhang/p/1493139.html
Copyright © 2011-2022 走看看