Trong C #, làm thế nào để kiểm tra xem có cổng TCP không?


120

Trong C # để sử dụng TcpClient hoặc nói chung để kết nối với ổ cắm, làm cách nào để kiểm tra trước tiên nếu một cổng nào đó còn trống trên máy của tôi?

thông tin thêm: Đây là mã tôi sử dụng:

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

2
Bạn hiểu "miễn phí" ở đây là gì? Nếu bạn cố gắng kết nối với nó và không thành công, điều đó có nghĩa là không có gì đang nghe.
Jon Skeet

2
Ý tôi là nó không được sử dụng bởi bất kỳ ứng dụng nào khác. Nếu một ứng dụng đang sử dụng một cổng, những người khác không thể sử dụng nó cho đến khi nó trở nên miễn phí.
Ali

Máy chủ bạn đang cố gắng kết nối là gì? Bạn đã viết nó, hay nó là một máy chủ hiện có?
đau buồn

3
Như đã được nêu trong nhiều câu trả lời, bạn có mọi thứ ngược lại. Cổng cục bộ và cổng từ xa là hai thứ hoàn toàn khác nhau. Câu trả lời này đặc biệt giải thích nó: stackoverflow.com/questions/570098/…
bzlm

Câu trả lời:


217

Vì bạn đang sử dụng a TcpClient, điều đó có nghĩa là bạn đang kiểm tra các cổng TCP đang mở. Có rất nhiều đối tượng tốt có sẵn trong không gian tên System.Net.NetworkInformation .

Sử dụng IPGlobalPropertiesđối tượng để truy cập một mảng TcpConnectionInformationđối tượng, sau đó bạn có thể thẩm vấn về IP và cổng của điểm cuối.


 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
Hãy nhớ rằng một quy trình khác có thể liên kết với cổng đó trong khi bạn đang kiểm tra.
Serguei

2
Điều này liên quan đến câu hỏi như thế nào? Câu trả lời này đề cập đến các cổng cục bộ , không phải các cổng từ xa .
bzlm

6
Nó có liên quan ở chỗ nó trả lời yêu cầu của OP. Mặc dù có sự khác biệt cục bộ / từ xa (hoàn toàn đồng ý với bạn), yêu cầu của OP là tạo một TcpClient mới với một cổng "miễn phí".
jro

8
Lưu ý rằng mã này chỉ kiểm tra các kết nối đang hoạt động. Các kết nối chưa gửi hoặc nhận gói tin sẽ không được liệt kê.
JoeBilly

29
So sánh kết quả của đoạn mã trên với netstat -a -b. Lưu ý rằng các LISTENINGkết nối không có trong GetActiveTcpConnections(). Bạn cũng nên đăng ký System.Net.IPEndPoints[]trả lại bởiipGlobalProperties.GetActiveTcpListeners();
Mike de Klerk

44

Bạn đang ở sai đầu của Intertube. Đây là máy chủ chỉ có thể mở một cổng cụ thể. Một số mã:

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

Không thành công với:

Thường chỉ cho phép sử dụng một địa chỉ socket (giao thức / địa chỉ mạng / cổng).


Đó là nơi tôi cũng sẽ đi với nó. Ai đó chắc chắn đang nhầm lẫn ở đây, và có thể đó là tôi.
Moose

3
Có thể máy khách cũng yêu cầu một cổng nhất định, nhưng nó không phổ biến.
Dave Van den Eynde

@DaveVandenEynde điều đó chỉ có thể xảy ra nếu "máy khách" thực sự đang "phục vụ" thứ gì đó trên cổng cụ thể đó (biến nó thành "máy chủ", ít nhất là đối với cổng đó, theo như TCP có liên quan). - Khi bạn có kết nối TCP của máy khách, bạn không thể kiểm soát cổng gửi đi. Ngăn xếp TCP chỉ định cho bạn một cổng đi và bạn không có bất kỳ quyền kiểm soát nào đối với nó.
BrainSlugs83

1
@ BrainSlugs83 Bạn chắc chắn nhất có thể: stackoverflow.com/questions/2869840/…
Dave Van den Eynde,

Đây là một đoạn mã hợp lệ để kiểm tra xem cổng có trống không. Chỉ cần nhớ Stop () TcpListener trong trường hợp nó thành công (trừ khi bạn định sử dụng cùng một đối tượng TcpListener đó để bắt đầu nghe trên cổng).
bgh

19

Khi bạn thiết lập kết nối TCP, 4-tuple (source-ip, source-port, dest-ip, dest-port) phải là duy nhất - điều này nhằm đảm bảo các gói được phân phối đến đúng nơi.

Có một hạn chế nữa ở phía máy chủ là chỉ một chương trình máy chủ có thể liên kết với số cổng đến (giả sử một địa chỉ IP; máy chủ nhiều NIC có các quyền hạn khác nhưng chúng ta không cần thảo luận ở đây).

Vì vậy, ở cuối máy chủ, bạn:

  • tạo một ổ cắm.
  • liên kết ổ cắm đó với một cổng.
  • nghe trên cổng đó.
  • chấp nhận các kết nối trên cổng đó. và có thể có nhiều kết nối đến (một kết nối cho mỗi khách hàng).

Ở phía khách hàng, nó thường đơn giản hơn một chút:

  • tạo một ổ cắm.
  • mở kết nối. Khi một máy khách mở kết nối, nó chỉ định địa chỉ ip và cổng của máy chủ . Nó có thể chỉ định cổng nguồn của nó nhưng thường sử dụng số không, dẫn đến hệ thống tự động gán cho nó một cổng miễn phí.

Không yêu cầu rằng IP / cổng đích phải là duy nhất vì điều đó sẽ dẫn đến chỉ một người có thể sử dụng Google tại một thời điểm và điều đó sẽ phá hủy khá nhiều mô hình kinh doanh của họ.

Điều này có nghĩa là bạn thậm chí có thể làm những điều kỳ diệu như FTP nhiều phiên vì bạn thiết lập nhiều phiên trong đó điểm khác biệt duy nhất là cổng nguồn của bạn, cho phép bạn tải xuống các phần mềm song song. Torrent có một chút khác biệt ở chỗ đích của mỗi phiên thường khác nhau.

Và, sau tất cả những điều đó (xin lỗi), câu trả lời cho câu hỏi cụ thể của bạn là bạn không cần chỉ định một cổng miễn phí. Nếu bạn đang kết nối với một máy chủ bằng một cuộc gọi không chỉ định cổng nguồn của bạn, thì gần như chắc chắn nó sẽ sử dụng số không dưới vỏ bọc và hệ thống sẽ cung cấp cho bạn một cổng không sử dụng.


15
TcpClient c;

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

... làm cách nào để kiểm tra trước tiên nếu một cổng nào đó còn trống trên máy của tôi?

Ý tôi là nó không được sử dụng bởi bất kỳ ứng dụng nào khác. Nếu một ứng dụng đang sử dụng một cổng, những người khác không thể sử dụng nó cho đến khi nó trở nên miễn phí. - Ali

Bạn đã hiểu sai những gì đang xảy ra ở đây.

Tham số TcpClient (...) là ip máy chủ và cổng máy chủ mà bạn muốn kết nối.

TcpClient chọn một cổng cục bộ tạm thời từ nhóm có sẵn để giao tiếp với máy chủ. Không cần phải kiểm tra tính khả dụng của cổng cục bộ vì nó được xử lý tự động bởi lớp winock.

Trong trường hợp bạn không thể kết nối với máy chủ bằng đoạn mã trên, sự cố có thể là một hoặc nhiều trong số nhiều. (tức là ip máy chủ và / hoặc cổng bị sai, máy chủ từ xa không khả dụng, v.v.)


15

Cảm ơn vì mẹo này. Tôi cần chức năng tương tự nhưng ở phía Máy chủ để kiểm tra xem Cổng có được sử dụng hay không, vì vậy tôi đã sửa đổi nó thành mã này.

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

Bạn không cần cái này. Chỉ cần chỉ định cổng số không.
Marquis of Lorne

Chú thích trong phần bình luận mã nguồn: "Đây là thông tin tương tự được cung cấp bởi ứng dụng dòng lệnh netstat" - là một lời nói dối. Nó không cung cấp thông tin giống như lệnh netstat cũng cung cấp PID của ứng dụng bằng cách sử dụng cổng ( -anb) và không có bất kỳ tham số nào, nó hiển thị kết nối nước ngoài cũng như trạng thái.
Kraang Prime

6

cảm ơn vì câu trả lời jro. Tôi đã phải điều chỉnh nó cho cách sử dụng của tôi. Tôi cần kiểm tra xem một cổng đang được lắng nghe và không hoạt động liên tục. Đối với điều này tôi đã thay thế

TcpConnectionInformation[] tcpConnInfoArray = ipGlobalProperties.GetActiveTcpConnections();

với

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

Tôi đã lặp lại mảng các điểm cuối để kiểm tra xem giá trị cổng của tôi không được tìm thấy.


Chỉ cần cố gắng lắng nghe nó cho chính mình. Bạn không cần bất kỳ điều này.
Marquis of 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);
}

Tôi là người đang cố gắng kết nối với một ổ cắm. Tôi đã chỉnh sửa câu hỏi để làm cho nó rõ ràng hơn.
Ali

Tôi thích đoạn mã này nhất, nó thực sự mở ra một kết nối TCP tới một ổ cắm chỉ để xem có ai đó đang nghe ở đầu bên kia hay không. Và tôi nghĩ rằng nhiều người thực sự muốn điều này, khi họ muốn "kiểm tra xem một cổng có được mở trên một máy chủ từ xa hay không". Tuy nhiên, có một lỗi trong đoạn mã trên: nếu (sock.Connected == true) thì cổng đang mở cho các kết nối, một số phần mềm giống như máy chủ đang lắng nghe ở đó. Nếu có một SocketException với 10061 ERRORCODE (message "(0x80004005): Không có kết nối có thể được thực hiện bởi vì các máy tính mục tiêu chủ động từ chối nó", sau đó các cổng được đóng lại!
Csaba Toth

Vì vậy, đó là cách khác trong đoạn mã. Một lưu ý khác: nếu ai đó muốn thử Dns.GetHostEntry trong thiết lập thử nghiệm như vậy nơi bạn có các máy chủ ảo chỉ có địa chỉ IP, bạn sẽ có một ngoại lệ.
Csaba Toth

3

netstat! Đó là một tiện ích dòng lệnh mạng đi kèm với các cửa sổ. Nó hiển thị tất cả các kết nối được thiết lập hiện tại và tất cả các cổng hiện đang được lắng nghe. Bạn có thể sử dụng chương trình này để kiểm tra, nhưng nếu bạn muốn thực hiện việc này từ mã, hãy xem không gian tên System.Net.NetworkInformation? Đó là một không gian tên mới kể từ 2.0. Có một số tiện ích ở đó. Nhưng cuối cùng nếu bạn muốn nhận được cùng một loại thông tin có sẵn thông qua lệnh netstat, bạn sẽ cần dẫn đến P / Invoke ...

Cập nhật: System.Net.NetworkInformation

Không gian tên đó chứa một loạt các lớp mà bạn có thể sử dụng để tìm hiểu những điều về mạng.

Tôi không thể tìm thấy đoạn mã cũ đó nhưng tôi nghĩ bạn có thể tự viết một cái gì đó tương tự. Một khởi đầu tốt là kiểm tra API trình trợ giúp IP . Google MSDN cho chức năng GetTcpTable WINAPI và sử dụng P / Invoke để liệt kê cho đến khi bạn có thông tin mình cần.


Tôi đang sử dụng .NET 3.5 và tôi không thể ổn điều đó.
Ali

Hãy để tôi sửa nó cho bạn: msdn.microsoft.com/en-us/library/…
bzlm

3

Nếu tôi không nhầm lẫn nhiều, bạn có thể sử dụng System.Network.w Anything để kiểm tra.

Tuy nhiên, điều này sẽ luôn luôn phát sinh một điều kiện chủng tộc.

Cách kiểm tra thông thường là cố gắng lắng nghe trên cổng đó. Nếu bạn gặp lỗi không mở được cổng.

Tôi nghĩ đây là một phần lý do tại sao bind () và listening () là hai lệnh gọi hệ thống riêng biệt.


2

Bạn nói

Ý tôi là nó không được sử dụng bởi bất kỳ ứng dụng nào khác. Nếu một ứng dụng đang sử dụng một cổng, những người khác không thể sử dụng nó cho đến khi nó trở nên miễn phí.

Nhưng bạn luôn có thể kết nối với một cổng trong khi những người khác đang sử dụng nó nếu có thứ gì đó đang nghe ở đó. Nếu không, cổng http 80 sẽ là một mớ hỗn độn.

Nếu là của bạn

   c = new TcpClient(ip, port);

không thành công, sau đó không có gì đang nghe ở đó. Nếu không, nó sẽ kết nối, ngay cả khi một số máy / ứng dụng khác có ổ cắm mở cho ip và cổng đó.


Nếu ứng dụng của tôi cố gắng kết nối bằng cổng 10 và một phiên bản khác của ứng dụng của tôi cố gắng kết nối lại bằng cổng 10, nó sẽ không thành công vì cả hai ứng dụng không thể gửi dữ liệu bằng một cổng, khi nhận dữ liệu tức là cổng 80 khác
Ali

Nó khác nhau như thế nào? những gì đang nghe trên cổng 10 cho ứng dụng của bạn?
Moose

nếu nó không thành công khi phiên bản thứ hai cố gắng kết nối, đó là ứng dụng đang nghe, không phải ứng dụng của bạn.
Moose

2

ipGlobalProperties.GetActiveTcpConnections() không trả về các kết nối trong Trạng thái Nghe.

Cổng có thể được sử dụng để nghe, nhưng không có ai kết nối với nó, phương pháp mô tả ở trên sẽ không hoạt động.


Phương pháp nào mô tả ở trên? Làm thế nào để trả lời câu hỏi này?
Marquis of Lorne

2

Từ các cổng có sẵn, tôi sẽ loại trừ:

  • kết nối TCP đang hoạt động
  • trình nghe TCP hoạt động
  • trình nghe UDP hoạt động

Với lần nhập sau:

using System.Net.NetworkInformation;

Bạn có thể sử dụng chức năng sau để kiểm tra xem một cổng có khả thi hay không:

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

Tôi cung cấp cho bạn một chức năng tương tự cho những người sử dụng 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

Để trả lời câu hỏi chính xác về việc tìm một cổng miễn phí (đó là những gì tôi cần trong các bài kiểm tra đơn vị của mình) trong dotnet core 3.1, tôi đã đưa ra điều này

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

lưu ý: dựa trên nhận xét của @ user207421 về cổng số 0, tôi đã tìm kiếm và tìm thấy điều này và đã sửa đổi một chút.


1

Hãy lưu ý khoảng thời gian giữa bạn thực hiện kiểm tra và thời điểm bạn cố gắng kết nối một số quá trình có thể mất cổng - TOCTOU cổ điển . Tại sao bạn không thử kết nối? Nếu nó không thành công thì bạn biết cổng không khả dụng.


4
TOCTOU là YALAIHHOBIKTPBI (Còn một chữ viết tắt dài nữa mà tôi chưa từng nghe qua, nhưng tôi biết hiện tượng đằng sau nó)
Csaba Toth vào

1

Bạn không cần phải biết cổng nào đang mở trên máy cục bộ của mình để kết nối với một số dịch vụ TCP từ xa (trừ khi bạn muốn sử dụng một cổng cục bộ cụ thể, nhưng thường thì không phải như vậy).

Mọi kết nối TCP / IP được xác định bằng 4 giá trị: IP từ xa, số cổng từ xa, IP cục bộ, số cổng cục bộ, nhưng bạn chỉ cần biết IP từ xa và số cổng từ xa để thiết lập kết nối.

Khi bạn tạo kết nối tcp bằng

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

Hệ thống của bạn sẽ tự động gán một trong nhiều số cổng cục bộ miễn phí cho kết nối của bạn. Bạn không cần phải làm gì cả. Bạn cũng có thể muốn kiểm tra xem một cổng từ xa có đang mở hay không. nhưng không có cách nào tốt hơn để làm điều đó ngoài việc cố gắng kết nối với nó.


Điều đó tốt cho việc kiểm tra một tập hợp các ổ cắm nhất định. Câu hỏi chỉ là về một ổ cắm (mặc dù số cổng nào có thể không được xác định (?))
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
Khi đăng một số mã, hãy cố gắng giải thích tại sao nó là giải pháp cho vấn đề. Chỉ mã có thể có quá ít thông tin và câu trả lời của bạn có nguy cơ bị xem qua.
Glubus

0

Kiểm tra mã lỗi 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

hãy thử điều này, trong trường hợp của tôi, số cổng cho đối tượng đã tạo không có sẵn vì vậy tôi đã nghĩ ra

IPEndPoint endPoint;
int port = 1;
while (true)
{
    try
    {
        endPoint = new IPEndPoint(IPAddress.Any, port);
        break;
    }
    catch (SocketException)
    {
         port++;
    }
}
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.