2010-04-20 42 views
149

我想啓動一個監聽端口的服務器。我可以明確指定端口並且它可以工作。但我想自動找到一個端口。在這方面我有兩個問題。如何查找可用端口?

  1. 我應該在哪個範圍的端口號搜索? (我用端口12345,12346和12347,這很好)。

  2. 如何才能知道給定的端口是否未被其他軟件佔用?

回答

219

如果您不介意使用的端口,請將端口指定爲0到ServerSocket constructor,它將偵聽任何空閒端口。

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

如果你想用一組特定的端口,那麼最簡單的方法可能是通過他們迭代,直到一個工作。事情是這樣的:

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

可以使用像這樣:

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"); 
} 
+1

使用新的ServerSocket(0)時,應注意關閉它!基於http:// javasourcecode。org/html/open-source/eclipse/eclipse-3.5.2/org/eclipse/jdt/launching/SocketUtil.java.html,稍微改編在我的https://gist.github.com/3429822 – vorburger 2012-08-22 22:09:59

+7

@vorburger,做它以這種方式很容易出現競賽狀況。更好地立即監聽服務器套接字,而不是打開它來查找空閒端口,再次關閉它,然後在空閒端口上再次打開一個端口(此時有一小部分機會現在正在監聽端口。) – 2012-09-04 12:49:50

+4

同意了,但它取決於確切的用例:在我的情況下,我需要找到一個自由端口號將它交給一些API(比如嵌入式Jetty啓動器,用於測試) - 相應的API需要一個套接字號 - 不是已經打開的服務器套接字。這取決於。 – vorburger 2012-09-14 11:44:55

2

如果您的服務器啓動,那麼沒有使用該套接字。

編輯

喜歡的東西:

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)); 
} 
+0

不要知道是誰投票給你,但我投票支持你。你可以設置一個遞歸函數來嘗試設置ServerSocket,如果你得到一個IOException(或者其他情況),你可以再試一次,直到成功獲取一個端口。 – jonescb 2010-04-20 13:22:14

+0

我認爲最好檢查一個端口是否可用,然後嘗試開始監聽這個端口。開始某件事情似乎並不高雅,然後發現存在一些問題,然後嘗試解決這些問題。 – Roman 2010-04-20 13:22:57

+2

@羅曼好,是的,那樣會更好,除了事實上沒有一種方法可以知道端口是否可用。如果'新的ServerSocket(0)'不適合你的替代方案是這樣的。我認爲,使用我的建議最終有85%的可能性。 – OscarRyz 2010-04-20 16:27:46

21

Wikipedia,你應該使用的端口4915265535如果你並不需要一個 '衆所周知' 的端口。

AFAIK確定是否正在使用端口的唯一方法是嘗試打開端口。

+0

+1。由於維基百科並不總是絕對真理/事實的來源,我認爲可能有用的是,該維基百科頁面的引用來自「互聯網號碼分配機構(IANA)的」[服務名稱和傳輸協議端口號註冊表(https://www.iana.org/assignments/service-names-port-numbers/service-names-port-numbers.xhtml)]「頁面,基於[RFC 6335 - 第6節](https:// tools.ietf.org/html/rfc6335#section-6)(即在這種情況下「固體」引用!:))。 – OzgurH 2017-08-24 09:30:09

48

如果您將0作爲端口號傳遞給ServerSocket的構造函數,它將爲您分配一個端口。

+0

是的,我認爲這是最優雅的解決方案,但由於某些原因,它不適用於我的情況。但我仍然需要找出究竟是什麼錯誤。 – Roman 2010-04-20 13:25:09

+0

@羅曼,發佈你的代碼,讓我們來找出 – OscarRyz 2010-04-20 13:29:41

+1

@羅曼:爲什麼它不起作用?更新您的問題以包含此問題(或者人們會不斷提出建議),並解釋爲什麼此解決方案不適合您。 – FrustratedWithFormsDesigner 2010-04-20 13:30:44

10

Eclipse SDK包含一個類SocketUtil,它可以做你想做的。你可以看看git source code

+1

看着Eclipse的代碼,他們做的事情與Graham Edgecombe的回答 – KevinL 2015-03-07 18:21:50

2

它可能沒有多大幫助,但在我的(Ubuntu)機器上,我有一個文件/ etc/services,其中至少有一些應用程序使用/保留的端口被給出。這些是這些應用程序的標準端口。

不保證這些應用程序正在運行,只是這些應用程序使用的默認端口(所以如果可能,您不應該使用它們)。

定義的端口略多於500個,大約一半的UDP和一半的TCP。

這些文件是使用IANA提供的信息製作的,請參閱IANA Assigned port numbers

+0

一樣,有一個類似的(更完整的IIRC)列表,它是nmap的一部分。 +1 – rmeador 2010-04-20 14:05:38

5

這對我的作品在Java 6

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

如果你想使用ServerSocket創建自己的服務器,你可以有它選擇一個空閒端口爲您提供:

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

其他服務器實現通常有類似的支持。 碼頭例如挑選一個空閒的端口,除非你明確地設置:

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

從Java 1.7開始,你可以使用try-與資源這樣的:

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

    } 
    } 

如果您需要查找特定接口上的開放端口,請查閱ServerSocket替代構造函數的文檔。

警告:使用此方法返回的端口號的任何代碼是受競爭情況 - 不同的進程/線程可能結合到同一個端口後我們關閉ServerSocket實例。

+0

如果您未設置「SO_REUSEADDR」,則這可能不起作用。還有一個競爭條件,但很難解決這個問題。 – 2015-06-29 04:40:21

+0

如果您隨後嘗試使用相同的端口號打開*另一個*'ServerSocket',則可能無法正常工作,因爲它可能在此期間被佔用。爲什麼你不會返回實際的'ServerSocket'而不是關閉它仍然是一個謎。 – EJP 2017-03-22 09:46:18

4

我最近剛剛發佈了一個小圖書館,只是考慮到了這一點。 Maven的依賴是:

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

一旦安裝,免費的端口號可以通過獲得:

int port = FreePortFinder.findFreeLocalPort(); 
15

如果您在範圍需要使用:

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

想知道如何檢查一個端口,然後解除綁定。謝謝SergeyB! – 2016-04-05 06:24:56

+2

如果[from,to]範圍內的每個端口都處於使用狀態,則此代碼將無限循環(或至少直到其中一個端口變爲空閒狀態)。如果您執行順序掃描而不是隨機選擇範圍內的端口,則可以避免出現這種可能性(只是在未找到空閒端口的情況下到達範圍末尾時發出異常)。如果您確實需要隨機選擇端口,那麼您需要一組記錄迄今嘗試過的端口,然後在該組的大小等於 - from時引發錯誤。 – 2016-12-15 05:33:19