Як в 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

Відповіді:


217

Оскільки ви використовуєте a 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
Майте на увазі, що під час перевірки інший процес може бути прив’язаний до цього порту.
Сергей

2
Наскільки це стосується питання? Ця відповідь стосується місцевих портів, а не віддалених портів.
bzlm

6
Він доречний тим, що відповідає запиту ОП. Незважаючи на локальну / віддалену різницю (повністю згоден з вами), вимога ОП полягала у створенні нового TcpClient з портом, який "вільний".
jro

8
Зауважте, що цей код перевіряє лише активні з'єднання. З'єднання, які не надіслали або отримали пакети, не будуть вказані.
JoeBilly

29
Порівняйте результат наведеного вище коду з netstat -a -b. Зауважте, що LISTENINGз'єднання відсутні в GetActiveTcpConnections(). Ви також повинні зареєструватися System.Net.IPEndPoints[]повернувipGlobalProperties.GetActiveTcpListeners();
Майк де Клерк

44

Ви не в тому кінці Інтертубе. Саме сервер може відкрити лише один конкретний порт. Деякі коди:

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

Не вдалося:

Зазвичай дозволяється лише одне використання кожної адреси сокета (протокол / мережева адреса / порт).


Ось де я йшов із цим. Хтось тут точно розгублений, і це можливо мені.
Мус

3
Можливо, що клієнт також вимагає певного порту, але це нечасто.
Дейв Ван ден Ейнде

@DaveVandenEynde, це може статися лише в тому випадку, якщо "клієнт" насправді "обслуговує" щось на цьому конкретному порту (що робить його "сервером", принаймні для цього порту, що стосується TCP). - Якщо у вас є клієнтське TCP-з'єднання, ви не можете контролювати вихідний порт. Стек TCP призначає вам вихідний порт, і ви не маєте ніякого контролю над ним.
BrainSlugs83

1
@ BrainSlugs83 Ви, безумовно, можете: stackoverflow.com/questions/2869840/…
Дейв Ван ден Ейнде

Це дійсний біт коду, щоб перевірити, чи вільний порт. Просто пам’ятайте, щоб зупинити () TcpListener у випадку успіху (якщо ви не збираєтесь використовувати той самий об’єкт TcpListener, щоб почати прослуховування через порт).
bgh

19

Під час налаштування TCP-з'єднання 4-кортеж (source-ip, source-port, dest-ip, dest-port) повинен бути унікальним - це гарантує доставку пакетів у потрібне місце.

На стороні сервера існує ще одне обмеження, що лише одна серверна програма може прив’язуватися до вхідного номера порту (припустимо, одна IP-адреса; багато-NIC-сервери мають інші повноваження, але нам тут не потрібно обговорювати їх).

Отже, на кінці сервера ви:

  • створити розетку.
  • прив’яжіть цю розетку до порту.
  • слухайте на тому порту.
  • прийняти з'єднання на цьому порту. і може бути кілька підключень (по одному на клієнта).

На клієнтському кінці, як правило, трохи простіше:

  • створити розетку.
  • відкрити з'єднання. Коли клієнт відкриває з'єднання, він вказує ip-адресу та порт сервера . Він може вказати свій вихідний порт, але зазвичай використовує нуль, що призводить до того, що система автоматично присвоює йому вільний порт.

Там немає НЕ потрібно, щоб призначення IP / порт бути унікальним , оскільки це призвело б тільки одна людина , в той час , будучи в змозі використати Google, і це буде дуже добре зруйнувати їхній бізнес - модель.

Це означає, що ви навіть можете робити такі дивовижні речі, як багатосесійний 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 також надає PID програми за допомогою порту ( -anb) і без будь-яких параметрів показує зовнішнє з'єднання, а також стан.
Прем'єр Кранг

6

дякую за відповідь jro. Мені довелося налаштувати це для мого використання. Мені потрібно було перевірити, чи слухається порт, а чи не обов’язково він активний. Для цього я замінив

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), то порт відкритий для з'єднань, там працює деяке серверне програмне забезпечення. Якщо є SocketException з кодом помилки 10061 (повідомлення "(0x80004005): Підключення не вдалося зробити, оскільки цільова машина активно відмовилась від нього", тоді порт закритий!
Csaba Toth,

Тож навпаки, у фрагменті коду. Ще одна примітка: якщо хтось спробує Dns.GetHostEntry у таких тестових установках, де у вас віртуальні сервери з лише IP-адресами, у вас буде виняток.
Csaba Toth

3

netstat! Це утиліта мережевого командного рядка, яка постачається з Windows. Він показує всі поточні встановлені з'єднання та всі порти, які зараз слухаються. Ви можете використовувати цю програму для перевірки, але якщо ви хочете зробити це з коду, загляньте у простір імен System.Net.NetworkInformation? Це новий простір імен, починаючи з 2.0. Там є кілька смаколиків. Але зрештою, якщо ви хочете отримати той самий вид інформації, який доступний через команду netstat, вам потрібно буде привести P / Invoke ...

Оновлення: System.Net.NetworkInformation

Цей простір імен містить купу класів, які можна використовувати для з'ясування речей із мережі.

Мені не вдалося знайти цю стару піску коду, але я думаю, що ви можете написати щось подібне самостійно. Хороший початок - перевірити IP Helper API . Google MSDN для функції WINAPI GetTcpTable та використовуйте P / Invoke для перерахування, поки не отримаєте необхідну інформацію.


Я використовую .NET 3.5, і не можу це виправити.
Алі

Дозвольте мені визначити
bzlm

3

Якщо я не дуже помиляюся, ви можете скористатися System.Network.What щоб перевірити.

Однак це завжди матиме умови для перегонів.

Канонічний спосіб перевірки - спробувати прослухати цей порт. Якщо ви отримаєте помилку, порт не був відкритий.

Я думаю, це частина того, чому bind () і liste () є двома окремими системними дзвінками.


2

Ти кажеш

Я маю на увазі, що він не використовується жодною іншою програмою. Якщо програма використовує порт, інші не можуть використовувати його, поки він не стане безкоштовним.

Але ви завжди можете підключитися до порту, тоді як інші користуються ним, якщо там щось слухають. Інакше http-порт 80 був би безлад.

Якщо ти

   c = new TcpClient(ip, port);

не вдається, то там нічого не слухають. В іншому випадку він підключиться, навіть якщо на якійсь іншій машині / додатку розетка відкрита до цього ip та порту.


Якщо моя програма спробує з'єднатись через порт 10 та інший екземпляр моєї програми, спробуйте підключитися за допомогою порту 10 знову, це не вдасться, оскільки обидва додатки не можуть надсилати дані за допомогою одного порту, коли мова заходить про отримання даних, тобто порт 80, який відрізняється
Алі

як це по-іншому? що слухає порт 10 для вашої програми?
Мус

якщо це не вдається, коли друга інстанція намагається підключитися, це програма прослуховування, а не ваша програма.
Мус

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 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 про нуль порту я шукав і знайшов це і трохи змінив його.


1

Будьте в курсі часового вікна між вами перевірки та моменту, коли ви намагаєтеся встановити з'єднання, якийсь процес може зайняти порт - класичний TOCTOU . Чому ви просто не спробуйте підключитися? Якщо це не вдалося, знаєте, порт не доступний.


4
TOCTOU - YALAIHHOBIKTPBI (ще одну довгу абревіатуру, яку я не чув, але я знаю явища за нею)
Csaba Toth,

1

Вам не потрібно знати, які порти відкриті на локальній машині для підключення до якоїсь віддаленої служби TCP (якщо ви не хочете використовувати певний локальний порт, але зазвичай це не так).

Кожне з'єднання TCP / IP ідентифікується 4 значеннями: віддалений IP, номер віддаленого порту, локальний IP, номер локального порту, але вам потрібно знати лише віддалений IP та номер віддаленого порту, щоб встановити з'єднання.

Коли ви створюєте tcp-з'єднання, використовуючи

TcpClient c;
c = новий TcpClient (remote_ip, remote_port);

Ваша система автоматично призначить вашому з’єднанню один із безлічі безкоштовних локальних портів. Вам нічого не потрібно робити. Ви також можете перевірити, чи відкритий віддалений порт. але немає кращого способу зробити це, ніж просто намагатися підключитися до нього.


Це добре для перевірки набору заданих розеток. Питання стосується лише одного сокета (який номер порту, можливо, не визначений, хоча (?))
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
Опублікувавши якийсь код, спробуйте детальніше пояснити, чому це рішення проблеми. Просто код може мати надто мало інформації, і ваша відповідь ризикує переглянути.
Глубус

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++;
    }
}
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.