动态取得本机可用的端口

该篇文章是我于2009年6月10日通过自己编写的工具,批量从位于在博客园的博客站点(http://chenxizhang.cnblogs.com)同步而来。文章中的图片地址仍然是链接到博客园的。特此说明!

陈希章

原文地址:http://www.cnblogs.com/chenxizhang/archive/2009/05/31/1493139.html
原文标题:动态取得本机可用的端口
原文发表:2009/5/31 10:19:00

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

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

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

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

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

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

///


/// 这个方法是验证某个端口是否可用
///

///
///
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();
}

 

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

///


/// 这个方法取得一个可用的端口
///

///
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
        {

            ///


            /// 这个方法是验证某个端口是否可用
            ///

            ///
            ///
            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;

            }

            ///


            /// 这个方法取得一个可用的端口
            ///

            ///
            public int GetAvaliablePort()
            {
                Random rnd = new Random();
                int port = rnd.Next(1024, 65535);//随机产生一个动态的端口号
                while (!IsAvaliable(port))
                {
                    port = rnd.Next(1024, 65535);
                }

                return port;
            }

        }
}

image

作者:陈希章
出处:http://blog.youkuaiyun.com/chen_xizhang
本文版权归作者所有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值