在C#中,如何检查TCP端口是否可用?


120

在C#中使用TcpClient或通常连接到套接字,如何首先检查计算机上某个端口是否空闲?

更多信息: 这是我使用的代码:

TcpClient c;
//I want to check here if port is free.
c = new TcpClient(ip, port);

2
您在这里所说的“免费”是什么意思?如果尝试连接到它并失败,则可能意味着没有声音在听。
乔恩·斯基特

2
我的意思是其他任何应用程序都没有使用它。如果应用程序正在使用端口,则其他应用程序必须等到它变得免费后才能使用。
阿里

您要连接的服务器是什么?您写的还是现有的服务器?
悲伤

3
正如许多答案中所述,您的一切都倒退了。本地端口和远程端口完全是两件事。这个答案特别能很好地解释它:stackoverflow.com/questions/570098/…–
bzlm

Answers:


217

由于您使用TcpClient,这意味着您正在检查打开的TCP端口。System.Net.NetworkInformation命名空间中有很多可用的好对象。

使用IPGlobalProperties对象获取对象数组,TcpConnectionInformation然后可以查询对象的端点IP和端口。


 int port = 456; //<--- This is your value
 bool isAvailable = true;

 // Evaluate current system tcp connections. This is the same information provided
 // by the netstat command line application, just in .Net strongly-typed object
 // form.  We will look through the list, and if our port we would like to use
 // in our TcpClient is occupied, we will set isAvailable to false.
 IPGlobalProperties ipGlobalProperties = IPGlobalProperties.GetIPGlobalProperties();
 TcpConnectionInformation[] tcpConnInfoArray = ipGlobalProperties.GetActiveTcpConnections();

 foreach (TcpConnectionInformation tcpi in tcpConnInfoArray)
 {
   if (tcpi.LocalEndPoint.Port==port)
   {
     isAvailable = false;
     break;
   }
 }

 // At this point, if isAvailable is true, we can proceed accordingly.

38
请记住,检查时另一个进程可能会绑定到该端口。
Serguei

2
这与问题有什么关系?此答案涉及本地端口,而不是远程端口。
bzlm

6
这很重要,因为它可以回答OP的请求。尽管存在本地/远程差异(完全同意您的观点),但是OP的要求是创建一个新的TcpClient,其端口为“免费”。
jro

8
请注意,此代码仅检查活动连接。没有发送或接收数据包的连接将不会列出。
JoeBilly 2011年

29
将以上代码的结果与进行比较netstat -a -b。请注意,LISTENING中没有连接GetActiveTcpConnections()。您还应该签入System.Net.IPEndPoints[]ipGlobalProperties.GetActiveTcpListeners();
Mike de Klerk,

44

您在Intertube的另一端。该服务器只能打开一个特定的端口。一些代码:

  IPAddress ipAddress = Dns.GetHostEntry("localhost").AddressList[0];
  try {
    TcpListener tcpListener = new TcpListener(ipAddress, 666);
    tcpListener.Start();
  }
  catch (SocketException ex) {
    MessageBox.Show(ex.Message, "kaboom");
  }

失败:

通常,每个套接字地址(协议/网络地址/端口)只能使用一种。


那也是我要去的地方。这里肯定有人感到困惑,可能是我。
Moose

3
客户端也可能需要某个端口,但这并不常见。
Dave Van den Eynde 09年

@DaveVandenEynde仅在“客户端”实际上在该特定端口上“服务”某些东西时才发生(至少就该端口而言,使其成为“服务器”,就TCP而言)。-当具有客户端TCP连接时,您无需控制传出端口。TCP堆栈为您分配了一个传出端口,您对此没有任何控制权。
BrainSlugs83 '16


这是检查端口是否空闲的有效代码。只要记住,请在成功的情况下停止Stop()TcpListener(除非您打算使用相同的TcpListener对象来开始在端口上侦听)。
bgh

19

设置TCP连接时,四元组(源IP,源端口,目标IP,目标端口)必须唯一-这是为了确保将数据包传递到正确的位置。

服务器端还有一个限制,即只有一个服务器程序可以绑定到传入的端口号(假设一个IP地址;多NIC服务器具有其他功能,但我们无需在此处进行讨论)。

因此,在服务器端,您:

  • 创建一个套接字。
  • 将该套接字绑定到端口。
  • 在那个端口上监听。
  • 接受该端口上的连接。并且可以有多个连接进入(每个客户端一个)。

在客户端,通常会更简单一些:

  • 创建一个套接字。
  • 打开连接。客户端打开连接时,它将指定服务器的IP地址和端口。它可以指定其源端口,但通常使用零,这将导致系统自动为其分配空闲端口。

没有要求,目标IP /端口是唯一的,因为这将导致只有一个人在同一时间能够使用谷歌,这将很好摧毁他们的商业模式。

这意味着您甚至可以执行多会话FTP之类的奇妙操作,因为您设置了多个会话,唯一的区别就是源端口,从而允许您并行下载块。洪流有点不同,因为每个会话的目的地通常是不同的。

而且,经过所有这些麻烦(对不起),对您的特定问题的答案是,您无需指定空闲端口。如果您通过未指定源端口的呼叫连接到服务器,则几乎可以肯定地使用了零,系统将为您提供未使用的端口。


15
TcpClient c;

//I want to check here if port is free.
c = new TcpClient(ip, port);

...如何首先检查机器上的某个端口是否空闲?

我的意思是其他任何应用程序都没有使用它。如果应用程序正在使用端口,则其他应用程序必须等到它变得免费后才能使用。–阿里

您误解了这里发生的事情。

TcpClient(...)参数包含您要连接的服务器IP和服务器端口。

TcpClient从可用池中选择一个临时本地端口以与服务器通信。由于Winsock层会自动处理本地端口,因此无需检查本地端口的可用性。

如果您无法使用上述代码片段连接到服务器,则问题可能出在其中的一个或多个。(即服务器IP和/或端口错误,远程服务器不可用,等等。)


15

感谢您的提示。我需要相同的功能,但需要在服务器端检查端口是否正在使用中,因此我将其修改为此代码。

 private bool CheckAvailableServerPort(int port) {
    LOG.InfoFormat("Checking Port {0}", port);
    bool isAvailable = true;

    // Evaluate current system tcp connections. This is the same information provided
    // by the netstat command line application, just in .Net strongly-typed object
    // form.  We will look through the list, and if our port we would like to use
    // in our TcpClient is occupied, we will set isAvailable to false.
    IPGlobalProperties ipGlobalProperties = IPGlobalProperties.GetIPGlobalProperties();
    IPEndPoint[] tcpConnInfoArray = ipGlobalProperties.GetActiveTcpListeners();

    foreach (IPEndPoint endpoint in tcpConnInfoArray) {
        if (endpoint.Port == port) {
            isAvailable = false;
            break;
        }
    }

    LOG.InfoFormat("Port {0} available = {1}", port, isAvailable);

    return isAvailable;
}

你不需要这个 只需指定端口零即可。
罗恩侯爵

源代码注释中的注释:“这是netstat命令行应用程序提供的相同信息” –是个谎言。它没有提供与netstat命令相同的信息,也没有提供使用端口(-anb)的应用程序的PID,并且不带任何参数,它显示了外部连接和状态。
Kraang Prime

6

谢谢你的回答。我必须对其进行调整以供使用。我需要检查是否正在监听端口,并且端口未处于活动状态。为此,我更换了

TcpConnectionInformation[] tcpConnInfoArray = ipGlobalProperties.GetActiveTcpConnections();

IPEndPoint[] objEndPoints = ipGlobalProperties.GetActiveTcpListeners();.  

我迭代了端点数组,以检查未找到我的端口值。


试着自己听。您不需要任何这些。
罗恩侯爵

5
string hostname = "localhost";
int portno = 9081;
IPAddress ipa = (IPAddress) Dns.GetHostAddresses(hostname)[0];


try
{
    System.Net.Sockets.Socket sock = new System.Net.Sockets.Socket(System.Net.Sockets.AddressFamily.InterNetwork, System.Net.Sockets.SocketType.Stream, System.Net.Sockets.ProtocolType.Tcp);
    sock.Connect(ipa, portno);
    if (sock.Connected == true)  // Port is in use and connection is successful
            MessageBox.Show("Port is Closed");
    sock.Close();

}
catch (System.Net.Sockets.SocketException ex)
{
    if (ex.ErrorCode == 10061)  // Port is unused and could not establish connection 
        MessageBox.Show("Port is Open!");
    else
        MessageBox.Show(ex.Message);
}

我是试图连接到套接字的人。我已经对问题进行了编辑,以使其更加清晰。
阿里

我最喜欢这个代码,它确实打开了到套接字的TCP连接,只是为了窥视是否有人在听另一端。而且我认为许多人在想“测试远程主机上的端口是否打开”时确实希望这样做。但是,上面的代码中存在一个错误:如果(sock.Connected == true),则说明该端口已打开以进行连接,某些类似于服务器的软件正在此处侦听。如果存在带有10061错误代码的SocketException(消息“(0x80004005):由于目标计算机主动拒绝了它,则无法建立连接”),则该端口已关闭!
Csaba Toth 2013年

因此,这是代码段中的另一种方式。另一个注意事项:如果有人在这样的测试设置中尝试Dns.GetHostEntry,而您的虚拟服务器仅具有IP地址,则将出现异常。
Csaba Toth 2013年

3

netstat!这是Windows附带的网络命令行实用程序。它显示所有当前建立的连接和当前正在侦听的所有端口。您可以使用此程序进行检查,但是如果要通过代码执行此操作,请查看System.Net.NetworkInformation命名空间?从2.0开始,这是一个新的命名空间。那里有一些好东西。但是最终,如果您想通过命令netstat获得相同类型的信息,则需要进行P / Invoke调用。

更新:System.Net.NetworkInformation

该名称空间包含一堆类,您可以使用这些类来了解有关网络的信息。

我找不到旧的代码,但我想您可以自己编写类似的东西。一个好的开始是检查IP Helper API。用于GetTcpTable WINAPI函数的Google MSDN, 并使用P / Invoke进行枚举,直到获得所需的信息为止。


我使用的是.NET 3.5,我无法接受。
阿里


3

如果我不是很误会,则可以使用System.Network.whatever进行检查。

但是,这总是会导致竞争状况。

规范的检查方法是尝试在该端口上侦听。如果出现错误,则说明该端口未打开。

认为这就是为什么bind()和listen()是两个单独的系统调用的一部分。


2

你说

我的意思是其他任何应用程序都没有使用它。如果应用程序正在使用端口,则其他应用程序必须等到它变得免费后才能使用。

但是,如果有人正在监听某个端口,那么您始终可以连接到该端口,而其他人正在使用它。否则,http端口80会一团糟。

如果你的

   c = new TcpClient(ip, port);

失败,那么那里什么也没听。否则,即使其他某些机器/应用程序的ip和端口都打开了套接字,它也会连接。


如果我的应用程序尝试使用端口10进行连接,而我的应用程序的另一个实例再次尝试使用端口10进行连接,则它将失败,因为在接收数据(即端口80不同)时,两个应用程序都无法使用一个端口发送数据
阿里

有什么不同?正在监听端口10的应用程序是什么?
Moose

如果第二个实例尝试连接时失败,则它是侦听应用程序,而不是您的应用程序。
Moose

2

ipGlobalProperties.GetActiveTcpConnections() 在监听状态下不返回连接。

可以将端口用于侦听,但是没有人连接到该端口时,上述方法将不起作用。


上述哪种方法?这如何回答这个问题?
罗恩侯爵

2

从可用的端口中,我将排除:

  • 活动的TCP连接
  • 活动的TCP侦听器
  • 主动UDP侦听器

使用以下导入:

using System.Net.NetworkInformation;

您可以使用以下功能来检查端口是否可用:

private bool isPortAvalaible(int myPort)
{
    var avalaiblePorts = new List<int>();
    var properties = IPGlobalProperties.GetIPGlobalProperties();

    // Active connections
    var connections = properties.GetActiveTcpConnections();
    avalaiblePorts.AddRange(connections);

    // Active tcp listners
    var endPointsTcp = properties.GetActiveTcpListeners();
    avalaiblePorts.AddRange(endPointsTcp);

    // Active udp listeners
    var endPointsUdp = properties.GetActiveUdpListeners();
    avalaiblePorts.AddRange(endPointsUdp);

    foreach (int p in avalaiblePorts){
        if (p == myPort) return false;
    }
    return true;
}

我为使用VB.NET的用户提供了类似的功能:

Imports System.Net.NetworkInformation
Private Function isPortAvalaible(ByVal myPort As Integer) As Boolean
  Dim props As IPGlobalProperties = IPGlobalProperties.GetIPGlobalProperties()

  ' ignore active connections
  Dim tcpConnInfoArray() As TcpConnectionInformation = props.GetActiveTcpConnections()
  For Each tcpi As Net.NetworkInformation.TcpConnectionInformation In tcpConnInfoArray
    If tcpi.LocalEndPoint.Port = myPort Then
      Return False
    End If
  Next tcpi

  ' ignore active TCP listeners
  Dim activeTcpListeners() As Net.IPEndPoint = props.GetActiveTcpListeners
  For Each tcpListener As Net.IPEndPoint In activeTcpListeners
    If tcpListener.Port = myPort Then
      Return False
    End If
  Next tcpListener

  ' ignore active UPD listeners
  Dim activeUdpListeners() As Net.IPEndPoint = props.GetActiveUdpListeners
  For Each udpListener As Net.IPEndPoint In activeUdpListeners
    If udpListener.Port = myPort Then
      Return False
    End If
  Next udpListener

  Return True
End Function

2

为了回答在dotnet core 3.1中找到可用端口(这是我的单元测试中所需要的端口)的确切问题,我提出了这一点

public static int GetAvailablePort(IPAddress ip) {
        TcpListener l = new TcpListener(ip, 0);
        l.Start();
        int port = ((IPEndPoint)l.LocalEndpoint).Port;
        l.Stop();
        Log.Info($"Available port found: {port}");
        return port;
    }

注意:根据@ user207421关于端口0的评论,我搜索并找到了端口并对其进行了少许修改。


1

请注意,从进行检查到尝试进行连接之间的时间窗口可能需要使用某些端口,例如经典的TOCTOU。您为什么不尝试连接?如果失败,则说明该端口不可用。


4
TOCTOU是YALAIHHOBIKTPBI(不过我还没听说过另一个长缩写,但我知道其背后的现象)
Csaba Toth,2013年

1

您不必知道在本地计算机上打开了哪些端口即可连接到某些远程TCP服务(除非您想使用特定的本地端口,但通常情况并非如此)。

每个TCP / IP连接都由4个值标识:远程IP,远程端口号,本地IP,本地端口号,但是您只需知道远程IP和远程端口号即可建立连接。

当您使用创建TCP连接时

TcpClient c;
c =新的TcpClient(remote_ip,remote_port);

您的系统将自动为您的连接分配许多可用的本地端口号之一。您什么都不需要做。您可能还想检查远程端口是否打开。但是没有比尝试连接它更好的方法了。


这对于检查一组给定的套接字很有用。问题仅涉及一个套接字(尽管可能不确定哪个端口号(?))
Csaba Toth 2013年

1
    public static bool TestOpenPort(int Port)
    {
        var tcpListener = default(TcpListener);

        try
        {
            var ipAddress = Dns.GetHostEntry("localhost").AddressList[0];

            tcpListener = new TcpListener(ipAddress, Port);
            tcpListener.Start();

            return true;
        }
        catch (SocketException)
        {
        }
        finally
        {
            if (tcpListener != null)
                tcpListener.Stop();
        }

        return false;
    }

0
test_connection("ip", port);


public void test_connection(String hostname, int portno) {
  IPAddress ipa = (IPAddress)Dns.GetHostAddresses(hostname)[0];
  try {
    System.Net.Sockets.Socket sock = new System.Net.Sockets.Socket(System.Net.Sockets.AddressFamily.InterNetwork, System.Net.Sockets.SocketType.Stream, System.Net.Sockets.ProtocolType.Tcp);
   sock.Connect(ipa, portno);
   if (sock.Connected == true) {
     MessageBox.Show("Port is in use");
   }

   sock.Close();
 }
 catch (System.Net.Sockets.SocketException ex) {
   if (ex.ErrorCode == 10060) {
     MessageBox.Show("No connection.");
   }
 }
}

1
发布某些代码时,请尝试详细说明为什么它可以解决问题。只是代码可能信息太少,您的答案可能会被仔细检查。
格卢布斯(Glubus)

0

检查错误代码10048

try
{
    TcpListener tcpListener = new TcpListener(ipAddress, portNumber);
    tcpListener.Start();
}
catch(SocketException ex)
{
    if(ex.ErrorCode == 10048)
    {
        MessageBox.Show("Port " + portNumber + " is currently in use.");
    }
    return;
}

-2

试试这个,在我的情况下,创建的对象的端口号不可用,所以我想到了这个

IPEndPoint endPoint;
int port = 1;
while (true)
{
    try
    {
        endPoint = new IPEndPoint(IPAddress.Any, port);
        break;
    }
    catch (SocketException)
    {
         port++;
    }
}
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.