In C #, come verificare se è disponibile una porta TCP?


120

In C # per usare un TcpClient o in generale per connettersi a un socket come posso prima verificare se una certa porta è libera sulla mia macchina?

maggiori informazioni: Questo è il codice che utilizzo:

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

2
Cosa intendi per "libero" qui? Se provi a connetterti ad esso e fallisci, probabilmente significa che nulla stava ascoltando.
Jon Skeet

2
Voglio dire che non è utilizzato da nessun'altra applicazione. Se un'applicazione utilizza una porta, gli altri non possono utilizzarla finché non diventa libera.
Ali

Qual è il server a cui stai tentando di connetterti? L'hai scritto o è un server esistente?
addolorato

3
Come è stato affermato in molte risposte, hai tutto al contrario. Le porte locali e le porte remote sono due cose completamente diverse. Questa risposta, in particolare, lo spiega bene: stackoverflow.com/questions/570098/...
bzlm

Risposte:


217

Dato che stai usando a TcpClient, significa che stai controllando le porte TCP aperte. Ci sono molti buoni oggetti disponibili nello spazio dei nomi System.Net.NetworkInformation .

Utilizzare l' IPGlobalPropertiesoggetto per accedere a un array di TcpConnectionInformationoggetti, che è quindi possibile interrogare sull'IP e sulla porta dell'endpoint.


 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
Tieni presente che un altro processo potrebbe legarsi a quella porta durante il controllo.
Serguei

2
In che modo questo è rilevante per la domanda? Questa risposta riguarda le porte locali , non le porte remote .
bzlm

6
È rilevante in quanto risponde alla richiesta del PO. Nonostante la differenza locale / remota (totalmente d'accordo con te), il requisito dell'OP era di creare un nuovo TcpClient con una porta "libera".
jro

8
Tieni presente che questo codice controlla solo le connessioni attive. Le connessioni che non hanno inviato o ricevuto pacchetti non verranno elencate.
JoeBilly

29
Confronta il risultato del codice sopra con netstat -a -b. Si noti che le LISTENINGconnessioni non sono presenti in GetActiveTcpConnections(). Dovresti anche fare il check-in System.Net.IPEndPoints[]restituito daipGlobalProperties.GetActiveTcpListeners();
Mike de Klerk

44

Sei dalla parte sbagliata dell'Intertube. È il server che può avere solo una particolare porta aperta. Alcuni codici:

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

Non riesce con:

Normalmente è consentito un solo utilizzo di ciascun indirizzo socket (protocollo / indirizzo di rete / porta).


È lì che stavo andando anche io. Qualcuno è decisamente confuso qui, ed è possibile che io.
Moose

3
È possibile che anche il client richieda una certa porta, ma non è comune.
Dave Van den Eynde

@DaveVandenEynde questo può accadere solo se il "client" sta effettivamente "servendo" qualcosa su quella particolare porta (rendendolo un "server", almeno per quella porta, per quanto riguarda il TCP). - Quando si dispone di una connessione TCP client, non è possibile controllare la porta in uscita. Lo stack TCP ti assegna una porta in uscita e non hai alcun controllo su di essa.
BrainSlugs83

1
@ BrainSlugs83 Puoi sicuramente: stackoverflow.com/questions/2869840/…
Dave Van den Eynde

Questo è un codice valido per verificare se la porta è libera. Ricorda solo di interrompere () il TcpListener nel caso in cui abbia successo (a meno che tu non intenda utilizzare lo stesso oggetto TcpListener per iniziare l'ascolto sulla porta).
bgh

19

Quando si imposta una connessione TCP, la 4-tupla (source-ip, source-port, dest-ip, dest-port) deve essere univoca - questo per garantire che i pacchetti siano consegnati nel posto giusto.

C'è un'ulteriore restrizione sul lato server che solo un programma server può legarsi a un numero di porta in entrata (assumendo un indirizzo IP; i server multi-NIC hanno altri poteri ma non abbiamo bisogno di discuterli qui).

Quindi, alla fine del server, tu:

  • creare un socket.
  • associare quel socket a una porta.
  • ascolta su quella porta.
  • accettare connessioni su quella porta. e possono esserci più connessioni in arrivo (una per client).

Sul lato client, di solito è un po 'più semplice:

  • creare un socket.
  • aprire la connessione. Quando un client apre la connessione, specifica l'indirizzo IP e la porta del server . Si può specificare la sua porta di origine ma solitamente utilizza zero, che risultati nel sistema assegnando una porta libera automaticamente.

Non v'è alcun requisito che la destinazione IP / porta essere unico dato che si tradurrebbe in una sola persona alla volta di essere in grado di usare Google, e che sarebbe abbastanza bene distruggere il loro modello di business.

Ciò significa che puoi persino fare cose meravigliose come l'FTP multisessione poiché hai impostato più sessioni in cui l'unica differenza è la tua porta sorgente, permettendoti di scaricare blocchi in parallelo. I torrent sono leggermente diversi in quanto la destinazione di ogni sessione è solitamente diversa.

E, dopo tutte queste chiacchiere (scusa), la risposta alla tua domanda specifica è che non è necessario specificare una porta libera. Se ti connetti a un server con una chiamata che non specifica la tua porta sorgente, quasi sicuramente utilizzerà zero sotto le coperte e il sistema te ne darà uno inutilizzato.


15
TcpClient c;

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

... come posso prima verificare se una certa porta è libera sulla mia macchina?

Voglio dire che non è utilizzato da nessun'altra applicazione. Se un'applicazione utilizza una porta, gli altri non possono utilizzarla finché non diventa libera. - Ali

Hai frainteso quello che sta succedendo qui.

I parametri di TcpClient (...) sono l'ip del server e la porta del server a cui desideri connetterti.

TcpClient seleziona una porta locale temporanea dal pool disponibile per comunicare con il server. Non è necessario verificare la disponibilità della porta locale poiché viene gestita automaticamente dal livello Winsock.

Nel caso in cui non sia possibile connettersi al server utilizzando il frammento di codice sopra, il problema potrebbe essere uno o più di diversi. (es. l'ip e / o la porta del server sono sbagliati, il server remoto non è disponibile, ecc.)


15

Grazie per questo suggerimento. Avevo bisogno della stessa funzionalità ma sul lato server per verificare se una porta era in uso, quindi l'ho modificata con questo codice.

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

Non ne hai bisogno. Basta specificare la porta zero.
Marchese di Lorne

La nota nel commento del codice sorgente: "Questa è la stessa informazione fornita dall'applicazione a riga di comando netstat" - è una bugia. Non fornisce le stesse informazioni come il comando netstat fornisce anche il PID dell'applicazione utilizzando port ( -anb) e senza parametri mostra la connessione esterna oltre allo stato.
Kraang Prime

6

grazie per la risposta jro. Ho dovuto modificarlo per il mio utilizzo. Avevo bisogno di controllare se una porta veniva ascoltata e non era necessariamente attiva. Per questo ho sostituito

TcpConnectionInformation[] tcpConnInfoArray = ipGlobalProperties.GetActiveTcpConnections();

con

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

Ho ripetuto l'array di endpoint verificando che il valore della mia porta non fosse trovato.


Prova solo ad ascoltarlo da solo. Non hai bisogno di niente di tutto questo.
Marchese di 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);
}

Sono quello che cerca di connettersi a una presa. Ho modificato la domanda per renderla più chiara.
Ali

Mi piace di più questo codice, questo apre davvero una connessione TCP a un socket solo per sbirciare se qualcuno sta ascoltando dall'altra parte o meno. E penso che molte persone lo vogliano davvero, quando vogliono "testare se una porta è aperta su un host remoto". Tuttavia c'è un bug nel codice precedente: se (sock.Connected == true) la porta è aperta per le connessioni, alcuni software simili a server sono in ascolto lì. Se è presente una SocketException con un ErrorCode 10061 (messaggio "(0x80004005): non è stato possibile stabilire alcuna connessione perché la macchina di destinazione l'ha rifiutata attivamente", la porta viene chiusa!
Csaba Toth

Quindi è il contrario rispetto allo snippet di codice. Un'altra nota: se qualcuno provasse Dns.GetHostEntry in tale configurazione di test in cui hai server virtuali con solo indirizzi IP, avrai un'eccezione.
Csaba Toth

3

netstat! Questa è un'utilità della riga di comando di rete fornita con Windows. Mostra tutte le connessioni correnti stabilite e tutte le porte attualmente in ascolto. Puoi usare questo programma per controllare, ma se vuoi farlo dal codice, guarda nello spazio dei nomi System.Net.NetworkInformation? È un nuovo spazio dei nomi a partire dalla 2.0. Ci sono alcune chicche lì. Ma alla fine, se vuoi ottenere lo stesso tipo di informazioni disponibili tramite il comando netstat, dovrai restituire P / Invoke ...

Aggiornamento: System.Net.NetworkInformation

Quello spazio dei nomi contiene un mucchio di classi che puoi usare per capire cose sulla rete.

Non sono riuscito a trovare quel vecchio pezzo di codice ma penso che tu possa scrivere qualcosa di simile da solo. Un buon inizio è controllare l' IP Helper API . Google MSDN per la funzione WINAPI GetTcpTable e utilizza P / Invoke per enumerare finché non hai le informazioni di cui hai bisogno.


Sto usando .NET 3.5 e non posso farlo bene.
Ali

Lasciatemi bene per voi: msdn.microsoft.com/en-us/library/...
bzlm

3

Se non mi sbaglio molto, puoi usare System.Network.wunque per controllare.

Tuttavia, ciò comporterà sempre una condizione di gara.

Il modo canonico di controllare è provare ad ascoltare su quella porta. Se ricevi un errore, la porta non era aperta.

Penso che questo sia parte del motivo per cui bind () e listen () sono due chiamate di sistema separate.


2

Tu dici

Voglio dire che non è utilizzato da nessun'altra applicazione. Se un'applicazione utilizza una porta, gli altri non possono utilizzarla finché non diventa libera.

Ma puoi sempre connetterti a una porta mentre altri la stanno usando se qualcosa sta ascoltando lì. Altrimenti, la porta http 80 sarebbe un disastro.

Se tuo

   c = new TcpClient(ip, port);

fallisce, quindi niente sta ascoltando lì. Altrimenti, si connetterà, anche se qualche altra macchina / applicazione ha un socket aperto a quell'ip e porta.


Se la mia applicazione tenta di connettersi utilizzando la porta 10 e un'altra istanza della mia applicazione tenta di connettersi nuovamente utilizzando la porta 10, fallirà perché entrambe le applicazioni non possono inviare dati utilizzando una porta, quando si tratta di ricevere dati, ad esempio la porta 80 che è diversa
Ali

come è diverso? cosa sta ascoltando sulla porta 10 per la tua applicazione?
Moose

se fallisce quando la seconda istanza tenta di connettersi, è l'applicazione in ascolto, non la tua applicazione.
Moose

2

ipGlobalProperties.GetActiveTcpConnections() non restituisce le connessioni nello stato di ascolto.

La porta può essere utilizzata per l'ascolto, ma senza nessuno connesso ad essa il metodo sopra descritto non funzionerà.


Quale metodo descritto sopra? Come risponde questo alla domanda?
Marchese di Lorne

2

Dalle porte disponibili escluderei:

  • connessioni TCP attive
  • listener TCP attivi
  • ascoltatori UDP attivi

Con la seguente importazione:

using System.Net.NetworkInformation;

È possibile utilizzare la seguente funzione per verificare se una porta è disponibile o meno:

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

Ti do una funzione simile per chi usa 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

Per rispondere alla domanda esatta di trovare una porta libera (che è ciò di cui avevo bisogno nei miei unit test) in dotnet core 3.1, ho trovato questo

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: in base al commento di @ user207421 sulla porta zero, ho cercato e trovato questo e l' ho leggermente modificato.


1

Fai attenzione all'intervallo di tempo che intercorre tra il controllo e il momento in cui provi a stabilire la connessione, alcuni processi potrebbero richiedere il port - il classico TOCTOU . Perché non provi a connetterti? Se fallisce, allora sai che la porta non è disponibile.


4
TOCTOU è YALAIHOBIKTPBI (ancora un'altra lunga abbreviazione di cui non ho sentito parlare, ma conosco i fenomeni dietro)
Csaba Toth

1

Non è necessario sapere quali porte sono aperte sulla macchina locale per connettersi a qualche servizio TCP remoto (a meno che non si desideri utilizzare una porta locale specifica, ma di solito non è così).

Ogni connessione TCP / IP è identificata da 4 valori: IP remoto, numero di porta remota, IP locale, numero di porta locale, ma è sufficiente conoscere l'IP remoto e il numero di porta remota per stabilire una connessione.

Quando crei una connessione TCP usando

TcpClient c;
c = nuovo TcpClient (remote_ip, remote_port);

Il sistema assegnerà automaticamente uno dei tanti numeri di porta locale gratuiti alla connessione. Non devi fare niente. Potresti anche voler controllare se una porta remota è aperta. ma non c'è modo migliore per farlo che provare a connettersi ad esso.


Questo è utile per controllare un insieme di prese date. La domanda riguarda un solo socket (quale numero di porta potrebbe essere indeterminato però (?))
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;
    }

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
Quando pubblichi del codice, prova ad elaborare il motivo per cui è la soluzione al problema. Solo il codice potrebbe contenere poche informazioni e la tua risposta rischia di essere esaminata.
Glubus

0

Verificare il codice di errore 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

prova questo, nel mio caso il numero di porta per l'oggetto creato non era disponibile, quindi ho pensato a questo

IPEndPoint endPoint;
int port = 1;
while (true)
{
    try
    {
        endPoint = new IPEndPoint(IPAddress.Any, port);
        break;
    }
    catch (SocketException)
    {
         port++;
    }
}
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.