W C #, jak sprawdzić, czy port TCP jest dostępny?


120

W C # do korzystania z TcpClient lub ogólnie do łączenia się z gniazdem, jak mogę najpierw sprawdzić, czy określony port jest wolny na moim komputerze?

więcej informacji: Oto kod, którego używam:

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

2
Co masz na myśli mówiąc „za darmo”? Jeśli spróbujesz się z nim połączyć i się nie powiedzie, prawdopodobnie oznacza to, że nic nie słuchało.
Jon Skeet

2
Chodzi mi o to, że nie jest używany przez żadną inną aplikację. Jeśli aplikacja korzysta z portu, inni nie mogą jej używać, dopóki nie stanie się wolna.
Ali

Z jakim serwerem próbujesz się połączyć? Napisałeś to, czy jest to istniejący serwer?
opłakiwać

3
Jak stwierdzono w wielu odpowiedziach, wszystko jest odwrócone. Porty lokalne i porty zdalne to dwie zupełnie różne rzeczy. W szczególności ta odpowiedź dobrze to wyjaśnia: stackoverflow.com/questions/570098/…
bzlm

Odpowiedzi:


217

Ponieważ używasz a TcpClient, oznacza to , że sprawdzasz otwarte porty TCP. W przestrzeni nazw System.Net.NetworkInformation dostępnych jest wiele dobrych obiektów .

Użyj IPGlobalPropertiesobiektu, aby dostać się do tablicy TcpConnectionInformationobiektów, którą możesz następnie zapytać o adres IP i port punktu końcowego.


 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
Należy pamiętać, że podczas sprawdzania inny proces może powiązać się z tym portem.
Serguei

2
Jak to ma związek z pytaniem? Ta odpowiedź dotyczy portów lokalnych , a nie portów zdalnych .
bzlm

6
Ma to znaczenie, ponieważ odpowiada na prośbę OP. Bez względu na różnicę lokalną / zdalną (całkowicie się z tobą zgadzam), wymaganiem OP było utworzenie nowego TcpClient z portem, który jest „wolny”.
jro

8
Zauważ, że ten kod sprawdza tylko aktywne połączenia. Połączenia, które nie wysłały ani nie odebrały pakietów, nie zostaną wyświetlone.
JoeBilly

29
Porównaj wynik powyższego kodu z netstat -a -b. Zwróć uwagę, że LISTENINGpołączenia nie są obecne w GetActiveTcpConnections(). Powinieneś również zameldować się, który System.Net.IPEndPoints[]wróciłipGlobalProperties.GetActiveTcpListeners();
Mike de Klerk

44

Jesteś na złym końcu Intertube. Jest to serwer, który może mieć otwarty tylko jeden określony port. Jakiś kod:

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

Zawodzi z:

Zwykle dozwolone jest tylko jedno użycie każdego adresu gniazda (protokołu / adresu sieciowego / portu).


Tam też z tym szedłem. Ktoś jest tu zdecydowanie zdezorientowany i możliwe, że to ja.
Łoś

3
Możliwe, że klient również wymaga określonego portu, ale nie jest to powszechne.
Dave Van den Eynde

@DaveVandenEynde może się to zdarzyć tylko wtedy, gdy "klient" faktycznie "obsługuje" coś na tym konkretnym porcie (czyniąc go "serwerem", przynajmniej dla tego portu, jeśli chodzi o TCP). - Kiedy masz połączenie TCP klienta, nie możesz kontrolować portu wychodzącego. Stos TCP przypisuje ci port wychodzący i nie masz nad nim żadnej kontroli.
BrainSlugs83

1
@ BrainSlugs83 Z całą pewnością możesz: stackoverflow.com/questions/2869840/…
Dave Van den Eynde

To jest poprawny fragment kodu do sprawdzenia, czy port jest wolny. Pamiętaj tylko, aby zatrzymać () TcpListener na wypadek, gdyby się powiedzie (chyba że zamierzasz użyć tego samego obiektu TcpListener do rozpoczęcia nasłuchiwania na porcie).
bgh

19

Podczas konfigurowania połączenia TCP 4 krotki (źródłowy adres IP, źródłowy port, docelowy adres IP, docelowy port) muszą być unikalne - ma to na celu zapewnienie, że pakiety są dostarczane we właściwe miejsce.

Istnieje dalsze ograniczenie po stronie serwera, że tylko jeden program serwera może połączyć się z numerem portu przychodzącego (zakładając jeden adres IP; serwery z wieloma kartami sieciowymi mają inne uprawnienia, ale nie musimy ich tutaj omawiać).

Tak więc po stronie serwera:

  • utwórz gniazdo.
  • połącz to gniazdo z portem.
  • słuchaj na tym porcie.
  • akceptuje połączenia na tym porcie. i może przychodzić wiele połączeń (jedno na klienta).

Po stronie klienta jest to zwykle trochę prostsze:

  • utwórz gniazdo.
  • otwórz połączenie. Gdy klient otwiera połączenie, określa adres IP i port serwera . To może określić swój port źródłowy, ale zwykle używa zerowe wyniki, które w systemie automatycznie przypisując mu wolny port.

Nie ma wymogu, aby docelowy adres IP / port był niepowtarzalny, ponieważ w danym momencie tylko jedna osoba mogłaby korzystać z Google, a to całkiem nieźle zniszczyłoby jej model biznesowy.

Oznacza to, że możesz nawet robić tak cudowne rzeczy, jak wielosesyjne FTP, ponieważ konfigurujesz wiele sesji, w których jedyną różnicą jest port źródłowy, umożliwiając równoległe pobieranie fragmentów. Torrenty różnią się nieco, ponieważ miejsce docelowe każdej sesji jest zwykle inne.

I po tym wszystkim (przepraszam), odpowiedź na twoje konkretne pytanie jest taka, że ​​nie musisz określać wolnego portu. Jeśli łączysz się z serwerem za pomocą połączenia, które nie określa portu źródłowego, prawie na pewno będzie używać zera pod osłonami, a system da ci nieużywany.


15
TcpClient c;

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

... jak mogę najpierw sprawdzić, czy określony port jest wolny na moim komputerze?

Chodzi mi o to, że nie jest używany przez żadną inną aplikację. Jeśli aplikacja korzysta z portu, inni nie mogą jej używać, dopóki nie stanie się wolna. - Ali

Źle zrozumiałeś, co się tutaj dzieje.

Parametry TcpClient (...) dotyczą adresu IP serwera i portu serwera, z którym chcesz się połączyć.

TcpClient wybiera przejściowy port lokalny z dostępnej puli do komunikacji z serwerem. Nie ma potrzeby sprawdzania dostępności portu lokalnego, ponieważ jest on automatycznie obsługiwany przez warstwę winsock.

W przypadku, gdy nie możesz połączyć się z serwerem przy użyciu powyższego fragmentu kodu, problem może dotyczyć jednego lub kilku z kilku. (tj. adres IP i / lub port serwera są nieprawidłowe, serwer zdalny jest niedostępny itp.)


15

Dzięki za tę wskazówkę. Potrzebowałem tej samej funkcjonalności, ale po stronie serwera, aby sprawdzić, czy port był używany, więc zmodyfikowałem go do tego kodu.

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

Nie potrzebujesz tego. Po prostu określ port zero.
Markiz Lorne

Uwaga w komentarzu do kodu źródłowego: „To są te same informacje, które dostarcza aplikacja wiersza poleceń netstat” - to kłamstwo. Nie dostarcza tych samych informacji, co polecenie netstat podaje również PID aplikacji za pomocą port ( -anb) i bez żadnych parametrów pokazuje połączenie obce oraz stan.
Kraang Prime

6

dzięki za odpowiedź jro. Musiałem go dostosować do własnego użytku. Musiałem sprawdzić, czy port jest nasłuchiwany i niekoniecznie aktywny. Na to wymieniłem

TcpConnectionInformation[] tcpConnInfoArray = ipGlobalProperties.GetActiveTcpConnections();

z

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

Wykonałem iterację tablicy punktów końcowych, sprawdzając, czy moja wartość portu nie została znaleziona.


Po prostu spróbuj sam tego posłuchać. Nie potrzebujesz tego.
Markiz 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);
}

To ja próbuję połączyć się z gniazdem. Zredagowałem pytanie, aby było jaśniejsze.
Ali

Najbardziej podoba mi się ten kod, to naprawdę otwiera połączenie TCP do gniazda tylko po to, aby sprawdzić, czy ktoś nasłuchuje na drugim końcu, czy nie. I myślę, że wiele osób naprawdę tego chce, kiedy chcą „sprawdzić, czy port jest otwarty na zdalnym hoście”. Jednak w powyższym kodzie jest błąd: jeśli (sock.Connected == true) to port jest otwarty dla połączeń, nasłuchuje tam oprogramowanie podobne do serwera. Jeśli istnieje wyjątek SocketException z kodem błędu 10061 (komunikat „(0x80004005): Nie można nawiązać połączenia, ponieważ maszyna docelowa aktywnie odmówiła”, wówczas port jest zamknięty!
Csaba Toth

Więc jest odwrotnie niż w przypadku fragmentu kodu. Kolejna uwaga: jeśli ktoś spróbuje Dns.GetHostEntry w takiej konfiguracji testowej, w której masz serwery wirtualne z tylko adresami IP, będziesz miał wyjątek.
Csaba Toth

3

netstat! To narzędzie wiersza poleceń sieci, które jest dostarczane z systemem Windows. Pokazuje wszystkie aktualnie ustanowione połączenia i wszystkie aktualnie nasłuchiwane porty. Możesz użyć tego programu do sprawdzenia, ale jeśli chcesz to zrobić z poziomu kodu, zajrzyj do przestrzeni nazw System.Net.NetworkInformation? Jest to nowa przestrzeń nazw od 2.0. Jest tam kilka smakołyków. Ale w końcu, jeśli chcesz uzyskać ten sam rodzaj informacji, który jest dostępny za pomocą polecenia netstat, będziesz potrzebować wyniku P / Invoke ...

Aktualizacja: System.Net.NetworkInformation

Ta przestrzeń nazw zawiera kilka klas, których możesz użyć do zorientowania się w sieci.

Nie udało mi się znaleźć tej starej części kodu, ale myślę, że sam możesz napisać coś podobnego. Na dobry początek sprawdź interfejs API pomocnika IP . Google MSDN dla funkcji GetTcpTable WINAPI i użyj P / Invoke do wyliczenia, aż uzyskasz potrzebne informacje.


Używam .NET 3.5 i nie mogę tego zrobić.
Ali

Pozwól mi to dla ciebie dobrze: msdn.microsoft.com/en-us/library/…
bzlm

3

Jeśli się nie mylę, możesz użyć System.Network.cokolwiek do sprawdzenia.

Jednak zawsze spowoduje to stan wyścigu.

Kanonicznym sposobem sprawdzenia jest próba nasłuchiwania na tym porcie. Jeśli pojawi się błąd, oznacza to, że port nie był otwarty.

Myślę, że jest to część tego, dlaczego bind () i Listen () to dwa oddzielne wywołania systemowe.


2

Mówisz

Chodzi mi o to, że nie jest używany przez żadną inną aplikację. Jeśli aplikacja korzysta z portu, inni nie mogą jej używać, dopóki nie stanie się wolna.

Ale zawsze możesz podłączyć się do portu, podczas gdy inni go używają, jeśli coś tam nasłuchuje. W przeciwnym razie port http 80 byłby bałaganem.

Jeżeli twój

   c = new TcpClient(ip, port);

zawiedzie, wtedy nic nie słucha. W przeciwnym razie połączy się, nawet jeśli inna maszyna / aplikacja ma gniazdo otwarte dla tego adresu IP i portu.


Jeśli moja aplikacja spróbuje połączyć się za pomocą portu 10, a inna instancja mojej aplikacji spróbuje połączyć się ponownie przy użyciu portu 10, zakończy się to niepowodzeniem, ponieważ obie aplikacje nie mogą wysyłać danych za pomocą jednego portu, jeśli chodzi o odbieranie danych, tj. Port 80, który jest inny
Ali

jak to się różni? co nasłuchuje na porcie 10 dla twojej aplikacji?
Łoś

jeśli nie powiedzie się, gdy druga instancja próbuje się połączyć, jest to aplikacja nasłuchująca, a nie aplikacja.
Moose

2

ipGlobalProperties.GetActiveTcpConnections() nie zwraca połączeń w stanie nasłuchiwania.

Port może służyć do nasłuchu, ale jeśli nikt do niego nie jest podłączony, metoda opisana powyżej nie zadziała.


Która metoda opisana powyżej? Jak to odpowiada na pytanie?
Markiz Lorne

2

Z dostępnych portów wykluczyłbym:

  • aktywne połączenia TCP
  • aktywnych nasłuchiwaczy TCP
  • aktywnych słuchaczy UDP

Z następującym importem:

using System.Net.NetworkInformation;

Możesz użyć następującej funkcji, aby sprawdzić, czy port jest dostępny, czy nie:

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

Podobną funkcję daję tym, którzy używają 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

Aby odpowiedzieć na dokładne pytanie dotyczące znalezienia wolnego portu (czego potrzebowałem w moich testach jednostkowych) w dotnet core 3.1, wymyśliłem to

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

uwaga: na podstawie komentarza @ user207421 o porcie zero szukałem i znalazłem to i nieznacznie zmodyfikowałem.


1

Zwróć uwagę na przedział czasu między dokonaniem sprawdzenia a momentem próby nawiązania połączenia jakiś proces może zająć port - klasyczne TOCTOU . Dlaczego po prostu nie spróbujesz się połączyć? Jeśli się nie powiedzie, wiesz, że port jest niedostępny.


4
TOCTOU to YALAIHHOBIKTPBI (Yet Another Long Abbreviation I Haven not Heard Of, But I Know the Phenomena Behind It)
Csaba Toth

1

Nie musisz wiedzieć, które porty są otwarte na komputerze lokalnym, aby połączyć się z jakąś zdalną usługą TCP (chyba że chcesz użyć określonego portu lokalnego, ale zwykle tak nie jest).

Każde połączenie TCP / IP jest identyfikowane za pomocą 4 wartości: zdalny adres IP, zdalny numer portu, lokalny adres IP, lokalny numer portu, ale wystarczy znać zdalny adres IP i zdalny numer portu, aby nawiązać połączenie.

Podczas tworzenia połączenia TCP za pomocą

TcpClient c;
c = nowy TcpClient (zdalny_ip, zdalny_port);

Twój system automatycznie przypisze jeden z wielu bezpłatnych lokalnych numerów portów do połączenia. Nie musisz nic robić. Możesz również sprawdzić, czy port zdalny jest otwarty. ale nie ma lepszego sposobu na zrobienie tego niż próba połączenia się z nim.


To jest dobre do sprawdzenia zestawu danych gniazd. Pytanie dotyczy tylko jednego gniazda (który numer portu może być jednak nieokreślony (?))
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
Wysyłając jakiś kod, spróbuj wyjaśnić, dlaczego jest to rozwiązanie problemu. Sam kod może zawierać zbyt mało informacji, a Twoja odpowiedź może zostać przejrzana.
Glubus

0

Sprawdź kod błędu 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

spróbuj tego, w moim przypadku numer portu dla utworzonego obiektu nie był dostępny, więc wymyśliłem to

IPEndPoint endPoint;
int port = 1;
while (true)
{
    try
    {
        endPoint = new IPEndPoint(IPAddress.Any, port);
        break;
    }
    catch (SocketException)
    {
         port++;
    }
}
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.