En C #, ¿cómo comprobar si hay un puerto TCP disponible?

120

En C #, para usar un TcpClient o, en general, para conectarme a un socket, ¿cómo puedo verificar primero si un determinado puerto está libre en mi máquina?

más información: Este es el código que uso:

TcpClient c;
//I want to check here if port is free.
c = new TcpClient(ip, port);
Ali
fuente
2
¿Qué quieres decir con "gratis" aquí? Si intentas conectarte a él y fallas, probablemente significa que no hay nada escuchando.
Jon Skeet
2
Quiero decir que no está siendo utilizado por ninguna otra aplicación. Si una aplicación está usando un puerto, otros no pueden usarlo hasta que esté libre.
Ali
¿Cuál es el servidor al que está intentando conectarse? ¿Lo escribió usted o es un servidor existente?
duelo
3
Como se ha dicho en muchas respuestas, tienes todo al revés. Los puertos locales y los puertos remotos son dos cosas completamente diferentes. Esta respuesta en particular lo explica bien: stackoverflow.com/questions/570098/…
bzlm

Respuestas:

217

Dado que está utilizando un TcpClient, eso significa que está comprobando los puertos TCP abiertos. Hay muchos buenos objetos disponibles en el espacio de nombres System.Net.NetworkInformation .

Utilice el IPGlobalPropertiesobjeto para llegar a una matriz de TcpConnectionInformationobjetos, que luego puede interrogar sobre la IP y el puerto del punto final.


 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.
jro
fuente
38
Tenga en cuenta que otro proceso podría vincularse a ese puerto mientras realiza la verificación.
Serguei
2
¿Cómo es esto relevante para la pregunta? Esta respuesta trata sobre puertos locales , no puertos remotos .
bzlm
6
Es relevante porque responde a la solicitud del OP. A pesar de la diferencia local / remota (totalmente de acuerdo con usted), el requisito del OP era crear un nuevo TcpClient con un puerto que sea "gratuito".
jro
8
Tenga en cuenta que este código solo verifica las conexiones activas. No se enumerarán las conexiones que no hayan enviado o recibido paquetes.
JoeBilly
29
Compare el resultado del código anterior con netstat -a -b. Observe que las LISTENINGconexiones no están presentes en GetActiveTcpConnections(). También debe registrarse System.Net.IPEndPoints[]devuelto poripGlobalProperties.GetActiveTcpListeners();
Mike de Klerk
44

Estás en el extremo equivocado del intertubo. Es el servidor que solo puede tener abierto un puerto en particular. Algún código:

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

Falla con:

Normalmente, sólo se permite un uso de cada dirección de socket (protocolo / dirección de red / puerto).

Hans Passant
fuente
Ahí es a donde me dirigía yo también. Alguien está definitivamente confundido aquí, y es posible que sea yo.
Moose
3
Es posible que el cliente también requiera un puerto determinado, pero no es común.
Dave Van den Eynde
@DaveVandenEynde eso solo puede suceder si el "cliente" realmente está "sirviendo" algo en ese puerto en particular (convirtiéndolo en un "servidor", al menos para ese puerto, en lo que respecta a TCP). - Cuando tiene una conexión TCP de cliente, no puede controlar el puerto de salida. La pila TCP le asigna un puerto de salida y usted no tiene ningún control sobre él.
BrainSlugs83
1
@ BrainSlugs83 Ciertamente puede: stackoverflow.com/questions/2869840/…
Dave Van den Eynde
Este es un código válido para verificar si el puerto está libre. Solo recuerde detener () el TcpListener en caso de que tenga éxito (a menos que tenga la intención de usar ese mismo objeto TcpListener para comenzar a escuchar en el puerto).
bgh
19

Cuando configura una conexión TCP, la 4-tupla (source-ip, source-port, dest-ip, dest-port) tiene que ser única; esto es para garantizar que los paquetes se entreguen en el lugar correcto.

Existe una restricción adicional en el lado del servidor de que solo un programa de servidor puede vincularse a un número de puerto entrante (asumiendo una dirección IP; los servidores de múltiples NIC tienen otros poderes, pero no es necesario que los analicemos aquí).

Entonces, en el extremo del servidor, usted:

  • crear un enchufe.
  • enlazar ese socket a un puerto.
  • escucha en ese puerto.
  • aceptar conexiones en ese puerto. y puede haber varias conexiones (una por cliente).

En el lado del cliente, generalmente es un poco más simple:

  • crear un enchufe.
  • abre la conexión. Cuando un cliente abre la conexión, especifica la dirección IP y el puerto del servidor . Se puede especificar su puerto de origen, pero por lo general utiliza cero, lo que da como resultado el sistema le asigna un puerto libre de forma automática.

No existe ningún requisito de que la IP / puerto de destino sea único, ya que eso daría como resultado que solo una persona a la vez pudiera usar Google, y eso destruiría bastante bien su modelo de negocio.

Esto significa que incluso puede hacer cosas tan maravillosas como FTP de múltiples sesiones, ya que configura varias sesiones donde la única diferencia es su puerto de origen, lo que le permite descargar fragmentos en paralelo. Los torrents son un poco diferentes porque el destino de cada sesión suele ser diferente.

Y, después de todo ese parloteo (lo siento), la respuesta a su pregunta específica es que no necesita especificar un puerto libre. Si se está conectando a un servidor con una llamada que no especifica su puerto de origen, es casi seguro que usará cero debajo de las cubiertas y el sistema le dará uno sin usar.

paxdiablo
fuente
15
TcpClient c;

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

... ¿cómo puedo comprobar primero si un puerto determinado está libre en mi máquina?

Quiero decir que no está siendo utilizado por ninguna otra aplicación. Si una aplicación está usando un puerto, otros no pueden usarlo hasta que esté libre. - Ali

Has entendido mal lo que está pasando aquí.

Los parámetros de TcpClient (...) son la IP del servidor y el puerto del servidor al que desea conectarse.

TcpClient selecciona un puerto local transitorio del grupo disponible para comunicarse con el servidor. No es necesario verificar la disponibilidad del puerto local, ya que la capa winsock lo maneja automáticamente.

En caso de que no pueda conectarse al servidor utilizando el fragmento de código anterior, el problema podría ser uno o más de varios. (es decir, la IP y / o el puerto del servidor son incorrectos, el servidor remoto no está disponible, etc.)

Indy9000
fuente
15

Gracias por este consejo. Necesitaba la misma funcionalidad pero en el lado del servidor para verificar si un puerto estaba en uso, así que lo modifiqué a este código.

 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;
}
Melloware
fuente
No necesitas esto. Simplemente especifique el puerto cero.
Marqués de Lorne
La nota en el comentario del código fuente: "Esta es la misma información proporcionada por la aplicación de línea de comandos netstat" - es una mentira. No proporciona la misma información que el comando netstat, también proporciona el PID de la aplicación que usa el puerto ( -anb) y, sin ningún parámetro, muestra la conexión externa y el estado.
Kraang Prime
6

gracias por la respuesta jro. Tuve que modificarlo para mi uso. Necesitaba comprobar si se estaba escuchando un puerto y si no estaba necesariamente activo. Por esto reemplacé

TcpConnectionInformation[] tcpConnInfoArray = ipGlobalProperties.GetActiveTcpConnections();

con

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

Repetí la matriz de puntos finales comprobando que no se encontró el valor de mi puerto.

user336046
fuente
Intenta escucharlo tú mismo. No necesitas nada de esto.
Marqués de Lorne
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);
}
Aprendizaje
fuente
Soy el que intenta conectarme a un enchufe. He editado la pregunta para que quede más clara.
Ali
Me gusta más este código, esto realmente abre una conexión TCP a un socket solo para ver si alguien está escuchando en el otro extremo o no. Y creo que muchas personas realmente quieren esto, cuando quieren "probar si un puerto está abierto en un host remoto". Sin embargo, hay un error en el código anterior: si (sock.Connected == true) entonces el puerto está abierto para conexiones, algún software similar a un servidor está escuchando allí. Si hay una SocketException con un ErrorCode 10061 (mensaje "(0x80004005): No se pudo establecer la conexión porque la máquina de destino la rechazó activamente", entonces el puerto está cerrado
Csaba Toth
Así que es al revés en el fragmento de código. Otra nota: si alguien intentara Dns.GetHostEntry en dicha configuración de prueba en la que tiene servidores virtuales con solo direcciones IP, tendrá una excepción.
Csaba Toth
3

netstat! Esa es una utilidad de línea de comandos de red que se envía con Windows. Muestra todas las conexiones establecidas actualmente y todos los puertos que se están escuchando. Puede usar este programa para verificar, pero si desea hacer esto desde el código, busque en el espacio de nombres System.Net.NetworkInformation. Es un nuevo espacio de nombres a partir de 2.0. Hay algunas golosinas ahí. Pero eventualmente, si desea obtener el mismo tipo de información que está disponible a través del comando netstat, deberá obtener el resultado P / Invoke ...

Actualización: System.Net.NetworkInformation

Ese espacio de nombres contiene un montón de clases que puede usar para averiguar cosas sobre la red.

No pude encontrar ese viejo fragmento de código, pero creo que puedes escribir algo similar tú mismo. Un buen comienzo es comprobar la API IP Helper . Google MSDN para la función GetTcpTable WINAPI y use P / Invoke para enumerar hasta que tenga la información que necesita.

John Leidegren
fuente
Estoy usando .NET 3.5 y no puedo multar eso.
Ali
Déjame
multarte
3

Si no me equivoco mucho, puede usar System.Network.whatever para verificar.

Sin embargo, esto siempre implicará una condición de carrera.

La forma canónica de verificar es intentar escuchar en ese puerto. Si recibe un error, ese puerto no estaba abierto.

Creo que esto es parte de por qué bind () y listen () son dos llamadas al sistema separadas.

Joshua
fuente
2

Tu dices

Quiero decir que no está siendo utilizado por ninguna otra aplicación. Si una aplicación está usando un puerto, otros no pueden usarlo hasta que esté libre.

Pero siempre puede conectarse a un puerto mientras otros lo están usando si algo está escuchando allí. De lo contrario, el puerto http 80 sería un desastre.

Si tu

   c = new TcpClient(ip, port);

falla, entonces no hay nada escuchando allí. De lo contrario, se conectará, incluso si alguna otra máquina / aplicación tiene un enchufe abierto a esa ip y puerto.

Alce
fuente
Si mi aplicación intenta conectarse usando el puerto 10 y otra instancia de mi aplicación intenta conectarse usando el puerto 10 nuevamente, fallará porque ambas aplicaciones no pueden enviar datos usando un puerto, cuando se trata de recibir datos, es decir, el puerto 80 que es diferente
Ali
¿Cómo es diferente? ¿Qué está escuchando en el puerto 10 para su aplicación?
Moose
si falla cuando la segunda instancia intenta conectarse, es la aplicación que escucha, no su aplicación.
Moose
2

ipGlobalProperties.GetActiveTcpConnections() no devuelve conexiones en estado de escucha.

El puerto se puede utilizar para escuchar, pero sin nadie conectado a él, el método descrito anteriormente no funcionará.

Tevo D
fuente
¿Qué método se describe arriba? ¿Cómo responde esto a la pregunta?
Marqués de Lorne
2

De los puertos disponibles, excluiría:

  • conexiones TCP activas
  • oyentes TCP activos
  • oyentes activos de UDP

Con la siguiente importación:

using System.Net.NetworkInformation;

Puede utilizar la siguiente función para comprobar si un puerto está disponible o no:

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

Te doy una función similar para aquellos que usan 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
Simone
fuente
2

Para responder a la pregunta exacta de encontrar un puerto libre (que es lo que necesitaba en mis pruebas unitarias) en dotnet core 3.1, se me ocurrió esto

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

nota: basé el comentario de @ user207421 sobre el puerto cero, busqué y encontré esto y lo modifiqué ligeramente.

Condron
fuente
1

Tenga en cuenta la ventana de tiempo entre que realiza la verificación y el momento en que intenta realizar la conexión, es posible que algún proceso tome el puerto: TOCTOU clásico . ¿Por qué no intentas conectarte? Si falla, entonces sabrá que el puerto no está disponible.

ya23
fuente
4
TOCTOU es YALAIHHOBIKTPBI (Otra abreviatura larga de la que no he oído hablar, pero conozco los fenómenos que se esconden detrás de ella)
Csaba Toth
1

No tiene que saber qué puertos están abiertos en su máquina local para conectarse a algún servicio TCP remoto (a menos que desee usar un puerto local específico, pero generalmente ese no es el caso).

Cada conexión TCP / IP se identifica mediante 4 valores: IP remota, número de puerto remoto, IP local, número de puerto local, pero solo necesita conocer la IP remota y el número de puerto remoto para establecer una conexión.

Cuando crea una conexión tcp usando

TcpClient c;
c = nuevo TcpClient (ip_remoto, puerto_remoto);

Su sistema asignará automáticamente uno de los muchos números de puerto locales libres a su conexión. No necesitas hacer nada. También es posible que desee comprobar si hay un puerto remoto abierto. pero no hay mejor manera de hacerlo que simplemente intentando conectarse a él.

Michał Piaskowski
fuente
Eso es bueno para verificar un conjunto de sockets dados. La pregunta es sobre un solo socket (aunque el número de puerto puede estar indeterminado (?))
Csaba Toth
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;
    }
Martin.Martinsson
fuente
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.");
   }
 }
}
Yuriy Stanchev
fuente
1
Cuando publique algún código, intente explicar por qué es la solución al problema. El código puede ser muy poca información y su respuesta corre el riesgo de ser revisada.
Glubus
0

Verifique el código de error 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;
}
Mohsen
fuente
-2

intente esto, en mi caso, el número de puerto para el objeto creado no estaba disponible, así que se me ocurrió esto

IPEndPoint endPoint;
int port = 1;
while (true)
{
    try
    {
        endPoint = new IPEndPoint(IPAddress.Any, port);
        break;
    }
    catch (SocketException)
    {
         port++;
    }
}
shakram02
fuente