Ổ cắm: Khám phá tính khả dụng của cổng bằng Java


123

Làm cách nào để lập trình xác định tính khả dụng của một cổng trong một máy nhất định bằng Java?

tức là đã cho một số cổng, xác định xem nó đã được sử dụng hay chưa?.


2
Trong khi câu hỏi này là về cách nhận biết một cổng nhất định đã được sử dụng chưa, bạn có thể đã hạ cánh ở đây để cố gắng tìm cách lấy số cổng miễn phí, mà stackoverflow.com/questions/2675362/ (có liên kết với ý chính của tôi .github.com / 3429822 ) bao gồm tốt hơn.
vorburger

Cho mục đích gì? Nếu bạn chỉ muốn tìm một ổ cắm trống để nghe, chỉ cần xác định không. Bất kỳ kỹ thuật quét nào cũng chịu trách nhiệm với các vấn đề về cửa sổ thời gian: cổng có thể bị bỏ trống khi bạn quét và chiếm dụng khi bạn đi đến yêu cầu.
Hầu tước Lorne

Câu trả lời:


91

Đây là triển khai đến từ dự án lạc đà Apache :

/**
 * Checks to see if a specific port is available.
 *
 * @param port the port to check for availability
 */
public static boolean available(int port) {
    if (port < MIN_PORT_NUMBER || port > MAX_PORT_NUMBER) {
        throw new IllegalArgumentException("Invalid start port: " + port);
    }

    ServerSocket ss = null;
    DatagramSocket ds = null;
    try {
        ss = new ServerSocket(port);
        ss.setReuseAddress(true);
        ds = new DatagramSocket(port);
        ds.setReuseAddress(true);
        return true;
    } catch (IOException e) {
    } finally {
        if (ds != null) {
            ds.close();
        }

        if (ss != null) {
            try {
                ss.close();
            } catch (IOException e) {
                /* should not be thrown */
            }
        }
    }

    return false;
}

Họ cũng đang kiểm tra DatagramSocket để kiểm tra xem cổng có khả dụng trong UDP và TCP không.

Hi vọng điêu nay co ich.


5
Một biến thể đẹp của điều này nếu bạn có MINA là AvailablePort Downloader.getNextAv Available (1024) sẽ mang lại cho bạn cổng không đặc quyền tiếp theo.
Alain O'Dea

9
Điều này không bắt tất cả mọi thứ tuy nhiên. Tôi đã bị cắn bởi một nginx đang chạy trên TCP 8000 trong khi thông lệ trên báo cáo 8000 là có sẵn. Không biết tại sao - tôi nghi ngờ nginx thực hiện một số thứ lén lút (đây là trên OS X). Giải pháp cho tôi là làm như trên và cũng để mở một Ổ cắm mới ("localhost", cổng) sau đó trả về false nếu chúng ta không có ngoại lệ. Suy nghĩ?
Một phần mây

2
Vấn đề tương tự trên Windows đang cố gắng phát hiện nếu máy chủ SMTP cắt giấy đang chạy. Một giải pháp trong đó bạn mở một kết nối đến một cổng, thay vì cố gắng liên kết với nó (như được đề xuất bởi nhận xét của Partly Clodys và câu trả lời của Ivan) dường như đáng tin cậy hơn.
stian

4
Hấp dẫn. Cuộc gọi đến setReuseAddress () hoàn toàn vô nghĩa sau khi ổ cắm đã bị ràng buộc và nó cũng hoàn toàn trái với mục đích của mã.
Hầu tước Lorne

3
Mã này có thể gặp vấn đề về cửa sổ thời gian. Nếu mục tiêu là tạo ra một ServerSocketcổng nghe, thì đó là điều nên làm và trả lại ServerSocket. Tạo nó và sau đó đóng nó và trả lại nó không đảm bảo rằng ServerSocketcó thể tạo ra một cái tiếp theo bằng cổng đó. Ditto cho DatagramSocket.
Hầu tước Lorne

41

Đối với Java 7, bạn có thể sử dụng try-with-resource cho mã nhỏ gọn hơn:

private static boolean available(int port) {
    try (Socket ignored = new Socket("localhost", port)) {
        return false;
    } catch (IOException ignored) {
        return true;
    }
}

15
Điều này không kiểm tra xem tại cảng có sẵn. Nó kiểm tra xem nó có ở trạng thái LISTEN hay không, liệu địa chỉ IP có thể truy cập được không, v.v.
Hầu tước Lorne

1
Ngoài ra, thử nghiệm này khá chậm (gần một giây trên mỗi cổng).
JMax

nó có nên trả về false khi IOException bị bắt không?
Baroudi Safwen

@BaroudiSafwen Nó hoàn toàn phụ thuộc vào ngoại lệ thực sự là gì. Đối với ConnectException: 'connection refused', có nó sẽ trả lại sai. Đối với thời gian chờ, không có gì nó có thể trả về sẽ hợp lệ, vì câu trả lời thực tế không được biết đến. Đó là lý do tại sao kỹ thuật này là vô dụng cho mục đích này.
Hầu tước Lorne

36

Có vẻ như kể từ Java 7, câu trả lời của David Santamaria không còn hoạt động đáng tin cậy nữa. Tuy nhiên, có vẻ như bạn vẫn có thể sử dụng Ổ cắm một cách đáng tin cậy để kiểm tra kết nối.

private static boolean available(int port) {
    System.out.println("--------------Testing port " + port);
    Socket s = null;
    try {
        s = new Socket("localhost", port);

        // If the code makes it this far without an exception it means
        // something is using the port and has responded.
        System.out.println("--------------Port " + port + " is not available");
        return false;
    } catch (IOException e) {
        System.out.println("--------------Port " + port + " is available");
        return true;
    } finally {
        if( s != null){
            try {
                s.close();
            } catch (IOException e) {
                throw new RuntimeException("You should handle this error." , e);
            }
        }
    }
}

Tôi muốn kiểm tra xem máy chủ proxy có sẵn để thực hiện cuộc gọi thay mặt anh ấy không.
Dejell

1
Câu trả lời của DavidSantamaria không bao giờ có tác dụng. Không có gì để làm với Java 7.
Hầu tước Lorne

35

Nếu bạn không quá quan tâm đến hiệu suất, bạn luôn có thể thử nghe trên một cổng bằng lớp ServerSocket . Nếu nó ném một tỷ lệ cược ngoại lệ thì nó đang được sử dụng.

public static boolean isAvailable(int portNr) {
  boolean portFree;
  try (var ignored = new ServerSocket(portNr)) {
      portFree = true;
  } catch (IOException e) {
      portFree = false;
  }
  return portFree;
}

EDIT: Nếu tất cả những gì bạn đang cố gắng làm là chọn một cổng miễn phí thì new ServerSocket(0)sẽ tìm một cổng cho bạn.


Cuối cùng sử dụng để dọn dẹp (thử {...} bắt {...} cuối cùng {if (socket! = Null) socket.close ();}
Hosam Aly

Bạn cũng có thể đặt "portTaken = (socket == null);" vào cuối thay vì làm điều đó trong đánh bắt.
Hosam Aly

1
Tôi không cảm thấy điều đó nằm trong phạm vi dự định của câu hỏi của tác giả.
Spencer Ruport

1
Có cách nào để làm điều đó mà không cần thử / bắt không? Tôi sẽ không sử dụng bất kỳ cổng có sẵn nào cho mục đích của mình, nhưng lặp đi lặp lại qua số cổng và thử / bắt từng cổng cho đến khi tôi tìm thấy một cổng miễn phí thì hơi lãng phí. Không có phương thức 'boolean có sẵn (int)' ở đâu đó, mà chỉ cần kiểm tra?
Oren Shalev

27
SeverSocket mới (0) sẽ tự động chọn một cổng miễn phí.
Spencer Ruport

10

Giải pháp sau đây được lấy cảm hứng từ việc triển khai Spring-core (giấy phép Apache) của SocketUtils .

So với các giải pháp khác sử dụng Socket(...)nó khá nhanh (kiểm tra 1000 cổng TCP trong chưa đầy một giây):

public static boolean isTcpPortAvailable(int port) {
    try (ServerSocket serverSocket = new ServerSocket()) {
        // setReuseAddress(false) is required only on OSX, 
        // otherwise the code will not work correctly on that platform          
        serverSocket.setReuseAddress(false);
        serverSocket.bind(new InetSocketAddress(InetAddress.getByName("localhost"), port), 1);
        return true;
    } catch (Exception ex) {
        return false;
    }
}       

3

Các giải pháp dựa trên ổ cắm thử / bắt, có thể không mang lại kết quả chính xác (địa chỉ ổ cắm là "localhost" và trong một số trường hợp, cổng có thể bị "chiếm" không phải bởi giao diện loopback và ít nhất là trên Windows tôi đã thấy thử nghiệm này không thành công các tuyên bố giả mạo là có sẵn).

Có một thư viện thú vị tên là SIGAR , đoạn mã sau có thể nối bạn:

Sigar sigar = new Sigar();
int flags = NetFlags.CONN_TCP | NetFlags.CONN_SERVER | NetFlags.CONN_CLIENT;             NetConnection[] netConnectionList = sigar.getNetConnectionList(flags);
for (NetConnection netConnection : netConnectionList) {
   if ( netConnection.getLocalPort() == port )
        return false;
}
return true;

Tôi nhận được: "không có sigar-x86-winnt.dll trong java.l Library.path" Thật không may, nó yêu cầu tải xuống một số dll bổ sung không thực sự khả thi trong môi trường doanh nghiệp (tôi không có quyền kiểm soát hệ thống CI). Thật tệ .... Dù sao cũng cảm ơn.
uthomas

@uthomas Tôi đoán bạn vẫn có quyền kiểm soát gói triển khai ứng dụng của mình, nếu đây là trường hợp bạn luôn có thể cung cấp DLL / SOs thời gian chạy Sigar gần JAR của bạn và đặt đường dẫn lib JAVA một cách thực tế (thông qua hack đơn giản, bạn có thể ảnh hưởng đến nó ngay cả sau khi ứng dụng đã được JVM khởi chạy).
Shmil Con mèo

2

Dọn dẹp câu trả lời được chỉ ra bởi David Santamaria:

/**
 * Check to see if a port is available.
 *
 * @param port
 *            the port to check for availability.
 */
public static boolean isPortAvailable(int port) {
    try (var ss = new ServerSocket(port); var ds = new DatagramSocket(port)) {
        return true;
    } catch (IOException e) {
        return false;
    }
}

Đây vẫn là đối tượng của một tình trạng chủng tộc đã chỉ ra bởi user207421 trong các ý kiến để trả lời David Santamaria của (một cái gì đó có thể lấy các cổng sau phương pháp này đóng ServerSocketDatagramSocketvà lợi nhuận).


1

Trong trường hợp của tôi, nó đã giúp thử và kết nối với cổng - nếu dịch vụ đã có sẵn, nó sẽ đáp ứng.

    try {
        log.debug("{}: Checking if port open by trying to connect as a client", portNumber);
        Socket sock = new Socket("localhost", portNumber);          
        sock.close();
        log.debug("{}: Someone responding on port - seems not open", portNumber);
        return false;
    } catch (Exception e) {         
        if (e.getMessage().contains("refused")) {
            return true;
    }
        log.error("Troubles checking if port is open", e);
        throw new RuntimeException(e);              
    }

1
Không phải những gì đã được yêu cầu.
Hầu tước Lorne

0

Trong trường hợp của tôi, tôi đã phải sử dụng lớp DatagramSocket.

boolean isPortOccupied(int port) {
    DatagramSocket sock = null;
    try {
        sock = new DatagramSocket(port);
        sock.close();
        return false;
    } catch (BindException ignored) {
        return true;
    } catch (SocketException ex) {
        System.out.println(ex);
        return true;
    }
}

Đừng quên nhập trước

import java.net.DatagramSocket;
import java.net.BindException;
import java.net.SocketException;

Điều này hoạt động cho các cổng UDP. Câu hỏi dường như là về các cổng TCP.
Hầu tước Lorne

-2

Tôi đã thử một cái gì đó như thế này và nó hoạt động rất tốt với tôi

            Socket Skt;
            String host = "localhost";
            int i = 8983; // port no.

                 try {
                    System.out.println("Looking for "+ i);
                    Skt = new Socket(host, i);
                    System.out.println("There is a Server on port "
                    + i + " of " + host);
                 }
                 catch (UnknownHostException e) {
                    System.out.println("Exception occured"+ e);

                 }
                 catch (IOException e) {
                     System.out.println("port is not used");

                 }

1
Không phải những gì đã được yêu cầu.
Hầu tước Lorne

Và cũng là một rò rỉ ổ cắm.
Hầu tước Lorne
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.