En C #, comment vérifier si un port TCP est disponible?

120

En C # pour utiliser un TcpClient ou généralement pour se connecter à une socket, comment puis-je d'abord vérifier si un certain port est libre sur ma machine?

plus d'infos: Voici le code que j'utilise:

TcpClient c;
//I want to check here if port is free.
c = new TcpClient(ip, port);
Ali
la source
2
Qu'entendez-vous par «gratuit» ici? Si vous essayez de vous y connecter et que vous échouez, cela signifie probablement que rien n'écoutait.
Jon Skeet
2
Je veux dire qu'il n'est utilisé par aucune autre application. Si une application utilise un port, d'autres ne peuvent pas l'utiliser jusqu'à ce qu'il devienne libre.
Ali
À quel serveur essayez-vous de vous connecter? L'avez-vous écrit ou s'agit-il d'un serveur existant?
pleurer le
3
Comme cela a été dit dans de nombreuses réponses, vous avez tout à l'envers. Les ports locaux et les ports distants sont deux choses totalement différentes. Cette réponse en particulier l'explique bien: stackoverflow.com/questions/570098/…
bzlm

Réponses:

217

Puisque vous utilisez un TcpClient , cela signifie que vous vérifiez les ports TCP ouverts. Il existe de nombreux bons objets disponibles dans l' espace de noms System.Net.NetworkInformation .

Utilisez l' IPGlobalPropertiesobjet pour accéder à un tableau d' TcpConnectionInformationobjets, que vous pouvez ensuite interroger sur l'adresse IP et le port du point de terminaison.


 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
la source
38
Gardez à l'esprit qu'un autre processus peut se lier à ce port pendant la vérification.
Serguei
2
En quoi est-ce pertinent par rapport à la question? Cette réponse concerne les ports locaux et non les ports distants .
bzlm
6
Elle est pertinente en ce qu'elle répond à la demande du PO. Malgré la différence local / distant (tout à fait d'accord avec vous), l'exigence de l'OP était de créer un nouveau TcpClient avec un port "gratuit".
jro
8
Notez que ce code ne vérifie que les connexions actives. Les connexions qui n'ont pas envoyé ou reçu de paquets ne seront pas répertoriées.
JoeBilly
29
Comparez le résultat du code ci-dessus à netstat -a -b. Notez que les LISTENINGconnexions ne sont pas présentes dans le GetActiveTcpConnections(). Vous devriez également vous enregistrer System.Net.IPEndPoints[]retourné paripGlobalProperties.GetActiveTcpListeners();
Mike de Klerk
44

Vous êtes du mauvais côté de l'Intertube. C'est le serveur qui ne peut avoir qu'un seul port ouvert. Un peu de code:

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

Échoue avec:

Une seule utilisation de chaque adresse de socket (protocole / adresse réseau / port) est normalement autorisée.

Hans Passant
la source
C'est là que j'allais aussi. Quelqu'un est définitivement confus ici, et c'est possible pour moi.
Moose
3
Il est possible que le client ait également besoin d'un certain port, mais ce n'est pas courant.
Dave Van den Eynde
@DaveVandenEynde cela ne peut arriver que si le "client" est en train de "servir" quelque chose sur ce port particulier (ce qui en fait un "serveur", au moins pour ce port, en ce qui concerne TCP). - Lorsque vous avez une connexion TCP client, vous ne contrôlez pas le port sortant. La pile TCP vous attribue un port sortant, et vous n'avez aucun contrôle dessus.
BrainSlugs83
1
@ BrainSlugs83 Vous pouvez très certainement: stackoverflow.com/questions/2869840/…
Dave Van den Eynde
C'est un morceau de code valide pour vérifier si le port est libre. N'oubliez pas d'arrêter () le TcpListener au cas où il réussirait (sauf si vous avez l'intention d'utiliser ce même objet TcpListener pour commencer à écouter sur le port).
bgh
19

Lorsque vous configurez une connexion TCP, le 4-tuple (source-ip, source-port, dest-ip, dest-port) doit être unique - ceci afin de garantir que les paquets sont livrés au bon endroit.

Il y a une autre restriction sur le serveur côté laquelle un seul programme serveur peut se lier à un numéro de port entrant (en supposant une adresse IP; les serveurs multi-NIC ont d'autres pouvoirs mais nous n'avons pas besoin de les discuter ici).

Donc, côté serveur, vous:

  • créer une prise.
  • liez cette socket à un port.
  • écoutez sur ce port.
  • accepter les connexions sur ce port. et il peut y avoir plusieurs connexions entrantes (une par client).

Du côté du client, c'est généralement un peu plus simple:

  • créer une prise.
  • ouvrez la connexion. Lorsqu'un client ouvre la connexion, il spécifie l'adresse IP et le port du serveur . Il peut spécifier son port source mais utilise généralement zéro, ce qui oblige le système à lui attribuer automatiquement un port libre.

Il n'est pas nécessaire que l'adresse IP / le port de destination soit unique, car cela permettrait à une seule personne à la fois d'utiliser Google, et cela détruirait assez bien son modèle commercial.

Cela signifie que vous pouvez même faire des choses aussi merveilleuses que le FTP multi-session puisque vous configurez plusieurs sessions où la seule différence est votre port source, vous permettant de télécharger des morceaux en parallèle. Les torrents sont un peu différents en ce que la destination de chaque session est généralement différente.

Et, après tout cela (désolé), la réponse à votre question spécifique est que vous n'avez pas besoin de spécifier un port franc. Si vous vous connectez à un serveur avec un appel qui ne spécifie pas votre port source, il utilisera presque certainement zéro sous les couvertures et le système vous en donnera un inutilisé.

paxdiablo
la source
15
TcpClient c;

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

... comment puis-je d'abord vérifier si un certain port est libre sur ma machine?

Je veux dire qu'il n'est utilisé par aucune autre application. Si une application utilise un port, d'autres ne peuvent pas l'utiliser jusqu'à ce qu'il devienne libre. - Ali

Vous avez mal compris ce qui se passe ici.

Les paramètres TcpClient (...) sont l'IP du serveur et le port du serveur auquel vous souhaitez vous connecter.

Le TcpClient sélectionne un port local transitoire dans le pool disponible pour communiquer avec le serveur. Il n'est pas nécessaire de vérifier la disponibilité du port local car il est automatiquement géré par la couche winsock.

Dans le cas où vous ne pouvez pas vous connecter au serveur à l'aide du fragment de code ci-dessus, le problème peut être un ou plusieurs. (c'est-à-dire que l'adresse IP et / ou le port du serveur est incorrect, le serveur distant n'est pas disponible, etc.)

Indy9000
la source
15

Merci pour cette astuce. J'avais besoin de la même fonctionnalité mais du côté du serveur pour vérifier si un port était utilisé, je l'ai donc modifié en ce code.

 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
la source
Vous n'en avez pas besoin. Spécifiez simplement le port zéro.
Marquis de Lorne
La note dans le commentaire du code source: "Ce sont les mêmes informations fournies par l'application de ligne de commande netstat" - est un mensonge. Elle ne fournit pas les mêmes informations que la commande netstat fournit également le PID de l'application utilisant le port ( -anb) et sans aucun paramètre, elle affiche la connexion étrangère ainsi que l'état.
Kraang Prime
6

merci pour la réponse jro. J'ai dû le peaufiner pour mon usage. J'avais besoin de vérifier si un port était écouté, et pas nécessairement actif. Pour cela j'ai remplacé

TcpConnectionInformation[] tcpConnInfoArray = ipGlobalProperties.GetActiveTcpConnections();

avec

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

J'ai itéré le tableau des points finaux en vérifiant que ma valeur de port n'a pas été trouvée.

user336046
la source
Essayez simplement de l'écouter vous-même. Vous n'avez besoin de rien de tout cela.
Marquis 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);
}
Apprentissage
la source
Je suis celui qui essaie de se connecter à une prise. J'ai édité la question pour la rendre plus claire.
Ali
J'aime ce code le mieux, cela ouvre vraiment une connexion TCP à une socket juste pour voir si quelqu'un écoute à l'autre bout ou non. Et je pense que beaucoup de gens veulent en fait cela, lorsqu'ils veulent "tester si un port est ouvert sur un hôte distant". Il y a cependant un bogue dans le code ci-dessus: si (sock.Connected == true) alors le port est ouvert pour les connexions, un logiciel de type serveur y écoute. S'il y a une SocketException avec un ErrorCode 10061 (message "(0x80004005): Aucune connexion n'a pu être établie parce que la machine cible l'a activement refusée" alors le port est fermé!
Csaba Toth
C'est donc l'inverse dans l'extrait de code. Une autre note: si quelqu'un essayait Dns.GetHostEntry dans une telle configuration de test où vous avez des serveurs virtuels avec uniquement des adresses IP, vous aurez une exception.
Csaba Toth
3

netstat! C'est un utilitaire de ligne de commande réseau fourni avec Windows. Il montre toutes les connexions actuellement établies et tous les ports actuellement écoutés. Vous pouvez utiliser ce programme pour vérifier, mais si vous souhaitez le faire à partir du code, regardez dans l'espace de noms System.Net.NetworkInformation? C'est un nouvel espace de noms à partir de 2.0. Il y a quelques goodies là-bas. Mais finalement, si vous voulez obtenir le même type d'informations disponibles via la commande netstat, vous devrez obtenir P / Invoke ...

Mise à jour: System.Net.NetworkInformation

Cet espace de noms contient un tas de classes que vous pouvez utiliser pour comprendre des choses sur le réseau.

Je n'ai pas pu trouver ce vieux code mais je pense que vous pouvez écrire quelque chose de similaire vous-même. Un bon début est de consulter l' API IP Helper . Google MSDN pour la fonction GetTcpTable WINAPI et utilisez P / Invoke pour énumérer jusqu'à ce que vous ayez les informations dont vous avez besoin.

John Leidegren
la source
J'utilise .NET 3.5 et je ne peux pas très bien.
Ali
Permettez-moi de l' amender
bzlm
3

Si je ne me trompe pas beaucoup, vous pouvez utiliser System.Network.wwhat pour vérifier.

Cependant, cela entraînera toujours une condition de course.

La méthode canonique de vérification est d'essayer d'écouter sur ce port. Si vous obtenez une erreur, le port n'était pas ouvert.

Je pense que cela explique en partie pourquoi bind () et listen () sont deux appels système distincts.

Joshua
la source
2

Vous dites

Je veux dire qu'il n'est utilisé par aucune autre application. Si une application utilise un port, d'autres ne peuvent pas l'utiliser jusqu'à ce qu'il devienne libre.

Mais vous pouvez toujours vous connecter à un port pendant que d'autres l'utilisent si quelque chose y écoute. Sinon, le port http 80 serait un gâchis.

Si ton

   c = new TcpClient(ip, port);

échoue, alors rien n'y écoute. Sinon, il se connectera, même si une autre machine / application a une prise ouverte sur cette adresse IP et ce port.

élan
la source
Si mon application essaie de se connecter en utilisant le port 10 et qu'une autre instance de mon application essaie de se connecter à nouveau en utilisant le port 10, elle échouera car les deux applications ne peuvent pas envoyer de données en utilisant un port, quand il s'agit de recevoir des données, c'est-à-dire le port 80 qui est différent
Ali
en quoi est-ce différent? qu'est-ce qui écoute sur le port 10 pour votre application?
Moose
s'il échoue lorsque la deuxième instance tente de se connecter, il s'agit de l'application d'écoute, pas de votre application.
Moose
2

ipGlobalProperties.GetActiveTcpConnections() ne renvoie pas les connexions dans l'état d'écoute.

Le port peut être utilisé pour l'écoute, mais sans personne connectée, la méthode décrite ci-dessus ne fonctionnera pas.

Tevo D
la source
Quelle méthode décrite ci-dessus? Comment cela répond-il à la question?
Marquis de Lorne
2

Des ports disponibles, j'exclure:

  • connexions TCP actives
  • écouteurs TCP actifs
  • écouteurs UDP actifs

Avec l'importation suivante:

using System.Net.NetworkInformation;

Vous pouvez utiliser la fonction suivante pour vérifier si un port est disponible ou non:

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

Je vous donne une fonction similaire pour ceux qui utilisent 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
la source
2

Pour répondre à la question exacte de trouver un port libre (ce dont j'avais besoin dans mes tests unitaires) dans dotnet core 3.1, j'ai proposé ceci

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

note: basé sur le commentaire de @ user207421 sur le port zéro, j'ai cherché et trouvé ceci et l' ai légèrement modifié.

CCondron
la source
1

Soyez conscient de la fenêtre de temps entre vous faites la vérification et le moment où vous essayez d'établir une connexion, un processus peut prendre le port - TOCTOU classique . Pourquoi n'essayez-vous pas simplement de vous connecter? En cas d'échec, vous savez que le port n'est pas disponible.

ya23
la source
4
TOCTOU est YALAIHHOBIKTPBI (Encore une longue abréviation dont je n'ai pas entendu parler, mais je connais les phénomènes qui se cachent derrière)
Csaba Toth
1

Vous n'avez pas besoin de savoir quels ports sont ouverts sur votre machine locale pour vous connecter à un service TCP distant (sauf si vous souhaitez utiliser un port local spécifique, mais ce n'est généralement pas le cas).

Chaque connexion TCP / IP est identifiée par 4 valeurs: IP distante, numéro de port distant, IP local, numéro de port local, mais il vous suffit de connaître l'adresse IP distante et le numéro de port distant pour établir une connexion.

Lorsque vous créez une connexion TCP en utilisant

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

Votre système attribuera automatiquement l'un des nombreux numéros de port locaux gratuits à votre connexion. Vous n'avez rien à faire. Vous pouvez également vérifier si un port distant est ouvert. mais il n'y a pas de meilleur moyen de le faire que d'essayer simplement de s'y connecter.

Michał Piaskowski
la source
C'est bon pour vérifier un ensemble de sockets donnés. La question est à propos d'un seul socket (quel numéro de port peut être indéterminé (?))
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
la source
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
la source
1
Lorsque vous publiez du code, essayez d'expliquer pourquoi c'est la solution au problème. Le code peut être trop peu d'informations et votre réponse risque d'être examinée.
Glubus
0

Recherchez le code d'erreur 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
la source
-2

essayez ceci, dans mon cas, le numéro de port de l'objet créé n'était pas disponible, alors j'ai trouvé ceci

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