Làm thế nào để tìm một cổng có sẵn?


194

Tôi muốn bắt đầu một máy chủ lắng nghe một cổng. Tôi có thể chỉ định cổng rõ ràng và nó hoạt động. Nhưng tôi muốn tìm một cổng một cách tự động. Về mặt này tôi có hai câu hỏi.

  1. Tôi nên tìm trong phạm vi số cổng nào? (Tôi đã sử dụng các cổng 12345, 12346 và 12347 và nó vẫn ổn).

  2. Làm thế nào tôi có thể tìm ra nếu một cổng nhất định không bị chiếm bởi phần mềm khác?

Câu trả lời:


277

Nếu bạn không quan tâm đến cổng được sử dụng, hãy chỉ định một cổng 0 cho hàm tạo ServerSocket và nó sẽ lắng nghe trên bất kỳ cổng miễn phí nào.

ServerSocket s = new ServerSocket(0);
System.out.println("listening on port: " + s.getLocalPort());

Nếu bạn muốn sử dụng một bộ cổng cụ thể, thì cách dễ nhất có lẽ là lặp qua chúng cho đến khi một cổng hoạt động. Một cái gì đó như thế này:

public ServerSocket create(int[] ports) throws IOException {
    for (int port : ports) {
        try {
            return new ServerSocket(port);
        } catch (IOException ex) {
            continue; // try next port
        }
    }

    // if the program gets here, no port in the range was found
    throw new IOException("no free port found");
}

Có thể được sử dụng như vậy:

try {
    ServerSocket s = create(new int[] { 3843, 4584, 4843 });
    System.out.println("listening on port: " + s.getLocalPort());
} catch (IOException ex) {
    System.err.println("no available ports");
}

2
Khi sử dụng ServerSocket mới (0), cần cẩn thận để đóng nó! Dựa trên javasourcecode.org/html/open-source/eclipse/eclipse-3.5.2/org/,, được điều chỉnh một chút trong gist.github.com/3429822
vorburger

9
@vorburger, làm theo cách đó dễ bị điều kiện chủng tộc. Sẽ tốt hơn nếu chỉ nghe trên ổ cắm máy chủ ngay lập tức, thay vì mở nó để tìm một cổng miễn phí, đóng lại và sau đó mở lại một lần nữa trên cổng miễn phí (lúc đó có một cơ hội nhỏ đang nghe thấy điều đó cảng.)
Graham Edgecombe

7
đồng ý, nhưng nó phụ thuộc vào trường hợp sử dụng chính xác: Trong trường hợp của tôi, tôi cần tìm một số cổng miễn phí để đưa nó vào một số API (giả sử một trình khởi động Jetty nhúng, để kiểm tra) - API tương ứng muốn có số ổ cắm - chưa phải là số mở ổ cắm máy chủ. Vì vậy, nó phụ thuộc.
vorburger

4
@vorburger API hợp lý sẽ chấp nhận số 0 dưới dạng số cổng hợp lệ để nghe và sau đó sẽ cho bạn biết cổng thực tế đang được lắng nghe. Hovewer, không có nhiều API hợp lý: nhiều chương trình kiểm tra cụ thể cổng 0 được thông qua và từ chối nó ( ssh -D 127.0.0.0:0 ...? Không!), Điều này thực sự gây nản lòng. Chúng tôi đã phải vá khá nhiều thư viện / chương trình để sử dụng chúng cho chúng tôi.
Joker_vD

2
@vorburger Khi sử dụng bất kỳ cổng nào , cần cẩn thận để đóng cổng. new ServerSocket(0)không khác nhau về mặt này. Không có bất cứ điều gì trong liên kết đầu tiên của bạn về điều này. Liên kết thứ hai của bạn chỉ đơn thuần thể hiện thực hành kém. Khi bạn đã tạo xong, ServerSocketbạn nên sử dụng nó, không đóng nó và cố gắng sử dụng lại cùng số cổng. Tất cả đó là một sự lãng phí hoàn toàn thời gian, cũng như dễ bị tổn thương với các vấn đề cửa sổ thời gian.
Hầu tước Lorne

57

Nếu bạn chuyển 0 làm số cổng cho nhà xây dựng của ServerSocket, nó sẽ phân bổ một cổng cho bạn.


Vâng, tôi nghĩ đó là giải pháp tao nhã nhất nhưng vì một số lý do, nó không hoạt động trong trường hợp của tôi. Nhưng tôi vẫn cần tìm ra chính xác những gì sai.
La Mã

1
@Roman: Tại sao nó không hoạt động? Cập nhật câu hỏi của bạn để bao gồm điều này (hoặc mọi người sẽ tiếp tục đề xuất nó) và giải thích lý do tại sao giải pháp này thất bại cho bạn.
Thất vọngWithFormsDesigner

2
Làm thế nào để tôi tìm thấy cổng này từ khách hàng? ;)
ed22

34

Bắt đầu từ Java 1.7, bạn có thể sử dụng tài nguyên dùng thử như thế này:

  private Integer findRandomOpenPortOnAllLocalInterfaces() throws IOException {
    try (
        ServerSocket socket = new ServerSocket(0);
    ) {
      return socket.getLocalPort();

    }
  }

Nếu bạn cần tìm một cổng mở trên giao diện cụ thể, hãy kiểm tra tài liệu ServerSocket cho các nhà xây dựng thay thế.

Cảnh báo: Bất kỳ mã nào sử dụng số cổng được trả về bởi phương thức này đều phải tuân theo điều kiện cuộc đua - một quy trình / luồng khác nhau có thể liên kết với cùng một cổng ngay sau khi chúng tôi đóng phiên bản ServerSocket.


1
Điều này có thể không hoạt động nếu bạn không thiết lập SO_REUSEADDR. Và có một điều kiện cuộc đua, nhưng thật khó để khắc phục điều đó.
David Ehrmann

Điều này có thể không hoạt động nếu sau đó bạn cố gắng mở một cái khác ServerSocket có cùng số cổng, vì nó có thể đã được thực hiện trong khi đó. Tại sao bạn không trả lại thực tế ServerSocketthay vì đóng nó vẫn còn là một bí ẩn.
Hầu tước Lorne

5
@EJP Đôi khi mã bên thứ ba chấp nhận một cổng nhưng không phải chính ổ cắm.
Thuyền trưởng Man

@CaptainMan Chắc chắn, nhưng trong những trường hợp đó, cổng chắc chắn được coi là preallocated. Một cổng được phân bổ động phải được cung cấp cho khách hàng bằng một số cơ chế khác như dịch vụ đặt tên hoặc phát sóng hoặc truyền phát. Toàn bộ câu hỏi ở đây là không đúng.
Hầu tước Lorne

@ user207421 Mặc dù vậy, bạn không thể thay đổi mã bên thứ ba để chấp nhận ServerSocketthay vì một intcổng.
Thuyền trưởng Man

30

Theo Wikipedia , bạn nên sử dụng các cổng 49152để 65535nếu bạn không cần một cổng 'nổi tiếng'.

AFAIK cách duy nhất để xác định một cổng đang sử dụng là cố gắng mở nó.


6
+1. Vì Wikipedia không phải lúc nào cũng là nguồn của sự thật / sự thật tuyệt đối, tôi nghĩ rằng có thể hữu ích khi lưu ý rằng, tham chiếu cho trang Wikipedia đó đến từ "Cơ quan cấp số được gán Internet (IANA)" [Tên dịch vụ và số cổng giao thức vận chuyển Trang đăng ký ( iana.org/assignments/service-names-port-numbers/ nam ", dựa trên RFC 6335 - Mục 6 (tức là tham chiếu" Solid "trong trường hợp này! :)).
OzgurH

25

Nếu bạn cần trong phạm vi sử dụng:

public int nextFreePort(int from, int to) {
    int port = randPort(from, to);
    while (true) {
        if (isLocalPortFree(port)) {
            return port;
        } else {
            port = ThreadLocalRandom.current().nextInt(from, to);
        }
    }
}

private boolean isLocalPortFree(int port) {
    try {
        new ServerSocket(port).close();
        return true;
    } catch (IOException e) {
        return false;
    }
}

đã tự hỏi làm thế nào để kiểm tra một cổng, sau đó hủy liên kết với nó. Cảm ơn Sergei!
Vlad Ilie

5
Nếu mọi cổng trong phạm vi [từ, đến) đang được sử dụng, mã này sẽ lặp vô hạn (hoặc ít nhất là cho đến khi một trong các cổng đó trở nên miễn phí). Nếu bạn thực hiện quét tuần tự thay vì chọn các cổng trong phạm vi một cách ngẫu nhiên, bạn có thể tránh khả năng này (chỉ cần ném một ngoại lệ khi bạn đến cuối phạm vi mà không tìm thấy cổng miễn phí). Nếu bạn thực sự cần chọn các cổng một cách ngẫu nhiên, thì bạn cần một bộ để theo dõi các cổng bạn đã thử cho đến nay, và sau đó phát sinh lỗi khi kích thước của bộ đó bằng - từ.
Simon Kissane

Cổng 0 đơn giản hơn đáng kể và không yêu cầu tạo hai ServerSocketstrong trường hợp thành công và không gặp phải vấn đề về cửa sổ thời gian của mã này.
Hầu tước Lorne


11

SDK Eclipse chứa một lớp SocketUtil , đó là những gì bạn muốn. Bạn có thể xem mã nguồn git .


2
Nhìn vào mã của Eclipse, họ làm điều tương tự như câu trả lời của Graham Edgecombe
KevinL


6

Cái này hoạt động với tôi trên Java 6

    ServerSocket serverSocket = new ServerSocket(0);
    System.out.println("listening on port " + serverSocket.getLocalPort());

6

Gần đây tôi đã phát hành một thư viện nhỏ để làm việc đó với các bài kiểm tra trong tâm trí. Sự phụ thuộc của Maven là:

<dependency>
    <groupId>me.alexpanov</groupId>
    <artifactId>free-port-finder</artifactId>
    <version>1.0</version>
</dependency>

Sau khi cài đặt, số cổng miễn phí có thể được lấy qua:

int port = FreePortFinder.findFreeLocalPort();

4

Nếu máy chủ của bạn khởi động, thì ổ cắm đó đã không được sử dụng.

BIÊN TẬP

Cái gì đó như:

ServerSocket s = null ;

try { 
    s = new ServerSocket( 0 ); 
} catch( IOException ioe ){
   for( int i = START; i < END ; i++ ) try {
        s = new ServerSocket( i );
    } catch( IOException ioe ){}
}
// At this point if s is null we are helpless
if( s == null ) {
    throw new IOException(
       Strings.format("Unable to open server in port range(%d-%d)",START,END));
}

Không biết ai đã bình chọn cho bạn, nhưng tôi đã bầu bạn sao lưu. Bạn có thể thiết lập một hàm đệ quy để cố gắng thiết lập ServerSocket và nếu bạn nhận được IOException (hoặc bất cứ thứ gì), bạn thử lại cho đến khi thành công nhận được một cổng.
jonescb

Tôi nghĩ sẽ tốt hơn nếu kiểm tra xem một cổng có sẵn không và sau đó thử bắt đầu nghe cổng này. Có vẻ không phải là thanh lịch để bắt đầu một cái gì đó và sau đó phát hiện ra rằng có một số vấn đề và sau đó cố gắng giải quyết những vấn đề này.
La Mã

2
@Roman tốt, vâng, điều đó sẽ tốt hơn, ngoại trừ thực tế là không có phương pháp nào để biết nếu một cổng có sẵn. Nếu new ServerSocket(0)không làm việc cho sự thay thế của bạn là điều này. Tôi nghĩ rằng có 85% khả năng bạn sẽ sử dụng đề xuất của tôi.
OscarRyz

Mã này tiếp tục mở và rò rỉ ServerSocketsmãi mãi và chỉ giữ lại cái cuối cùng đã thành công. Nó cần một breaktuyên bố.
Hầu tước Lorne

4

Nếu bạn muốn tạo máy chủ của riêng mình bằng cách sử dụng ServerSocket, bạn có thể yêu cầu nó chọn một cổng miễn phí cho bạn:

  ServerSocket serverSocket = new ServerSocket(0);
  int port = serverSocket.getLocalPort();

Các triển khai máy chủ khác thường có hỗ trợ tương tự. Cầu tàu chẳng hạn chọn một cổng miễn phí trừ khi bạn đặt nó một cách rõ ràng:

  Server server = new Server();
  ServerConnector connector = new ServerConnector(server);
  // don't call: connector.setPort(port);
  server.addConnector(connector);
  server.start();
  int port = connector.getLocalPort();

3

Nó có thể không giúp bạn nhiều, nhưng trên máy (Ubuntu) của tôi, tôi có một tệp / etc / services trong đó ít nhất là các cổng được sử dụng / dành riêng cho một số ứng dụng được cung cấp. Đây là các cổng tiêu chuẩn cho các ứng dụng đó.

Không đảm bảo rằng các ứng dụng này đang chạy, chỉ các cổng mặc định mà các ứng dụng này sử dụng (vì vậy bạn không nên sử dụng chúng nếu có thể).

Có hơn 500 cổng được xác định, khoảng một nửa UDP và một nửa TCP.

Các tệp được tạo bởi thông tin của IANA, xem số cổng được gán của IANA .


có một danh sách tương tự (và đầy đủ hơn, IIRC) là một phần của nmap. +1
rmeador

3

Nếu bạn đang sử dụng Spring, câu trả lời do Michail Nikolaev cung cấp là câu trả lời đơn giản và gọn gàng nhất và IMO nên được nâng cấp. Chỉ nhằm mục đích thuận tiện, tôi sẽ thêm một ví dụ nội tuyến bằng cách sử dụng phương thức Springframwework SocketUtils .findAv AvailableTcpPort () :

int randomAvailablePort = SocketUtils.findAvailableTcpPort();

Nó dễ như vậy, chỉ một dòng thôi :). Tất nhiên, lớp Utils cung cấp nhiều phương thức thú vị khác, tôi khuyên bạn nên xem tài liệu.


1

Sử dụng lớp 'ServerSocket', chúng tôi có thể xác định xem cổng đã cho đang sử dụng hay miễn phí. ServerSocket cung cấp một hàm tạo lấy một số nguyên (là số cổng) làm đối số và khởi tạo ổ cắm máy chủ trên cổng. Nếu ServerSocket ném bất kỳ Ngoại lệ IO nào, thì chúng ta có thể giả sử cổng này đã được sử dụng.

Đoạn mã sau được sử dụng để có được tất cả các cổng có sẵn.

for (int port = 1; port < 65535; port++) {
         try {
                  ServerSocket socket = new ServerSocket(port);
                  socket.close();
                  availablePorts.add(port);
         } catch (IOException e) {

         }
}

Liên kết tham khảo .


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.