今天在項目中考慮這樣一件事情:我需要動態實例化一個服務,監聽某個端 口。那麼該怎麼來實現這個需求呢?
我立馬想到是否有這樣的函數,例 如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());
}
但是,讓人吃驚的是,我們看到下面這樣的輸出結果。
這是如何解釋呢?為什麼既然是隨機數,又仍然是重復呢?
這是因為 我們的10次循環之間幾乎沒有間隔(特別是我的機器配置又很好的情況下),所 以,對於隨機數的產生器,它實際上無法進行區分。我自己感覺就是,如果兩個 操作之間幾乎沒有時間間隔,那麼隨機數也是一樣的。
那麼該如何解決 該問題?好吧,我們可以刻意地讓每次循環有一些間隔
for (int i = 0; i < 10; i++)
{
Thread.Sleep (100);//我們強制讓進程休眠0.1秒
Console.WriteLine (GetAvaliablePort());
}
這樣大家總該是沒有意見了吧?
最後,我們對代碼進行一些重構,封 裝之後的完整代碼如下
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;
}
}
}