2010-07-16 3 views
6

Я понимаю, что следующий код может (возможно, не очень эффективно) найти свободный TCP-порт в Java:Нахождение двух свободных портов TCP

public static int findFreePort() { 
    int port; 
    try { 
     ServerSocket socket= new ServerSocket(0); 
     port = socket.getLocalPort(); 
     socket.close(); 
    } catch (Exception e) { port = -1; } 
    return port;  
    } 

(Есть некоторые смежные вопросы здесь SO - forexample) ,

То, что я не понимаю, - это то, почему (или есть) два успешных вызова этого метода гарантированно возвратят два разных порта. Предполагается, например, here (поиск звонков на метод findFreePort).

Это правильно?

+0

Если есть связанные вопросы, вы должны указать их и ссылаться на них. – Freiheit

+0

@Freiheit: Done – leonbloy

+0

Он не перераспределен из-за SO_WAIT, механизма, призванного обойти, что пакет, все еще находящийся в пути, будет получен другим процессом.По умолчанию, вы закрываете порт TCP, он не перераспределяется в течение следующих 2 минут, чтобы эти затяжные пакеты были сброшены. –

ответ

5

В спецификации Javadoc, я не вижу ни строки о том, что два succesive вызовов гарантированно возвращает два разных порта ...

Поскольку ServerSocket закрыт, второй вызов может дать один и тот же порт. По статистике, это невозможно, но не невозможно.

Если вы открываете два ServerSocket, получаете порты, а затем закрываете два ServerSocket, вы можете получить два разных порта (так как первый не является бесплатным при создании второго ServerSocket).

Пример использования метода, чтобы получить п различных свободных портов:

public int[] getFreePorts(int portNumber) throws IOException { 
    int[] result = new int[portNumber]; 
    List<ServerSocket> servers = new ArrayList<ServerSocket>(portNumber); 
    ServerSocket tempServer = null; 

    for (int i=0; i<portNumber; i++) { 
     try { 
      tempServer = new ServerSocket(0); 
      servers.add(tempServer); 
      result[i] = tempServer.getLocalPort(); 
     } finally { 
      for (ServerSocket server : servers) { 
       try { 
        server.close(); 
       } catch (IOException e) { 
        // Continue closing servers. 
       } 
      } 
     } 
    } 
    return result; 
} 
+0

Да, я только что закодировал почти точно такой же метод :-) – leonbloy

+1

Следует отметить, что даже это решение (и Noel M) не является на 100% надежным, существует потенциальное состояние гонки. После вызова этого метода вызывающий абонент попытается использовать эти доступные порты. Но может случиться так, что в то же время другой процесс открыл его. – leonbloy

0

Исходный код для ServerSocket здесь: http://kickjava.com/src/java/net/ServerSocket.java.htm

Я не совсем, видя, как он определяет, будет ли порт свободен или нет, но:

@param port the port number, or <code>0</code> to use any 
free port. 

Так как только первый порт выделяется, даже для вашего приложения, он больше не является бесплатным. Поэтому последовательные вызовы ServerSocket не будут повторно использовать этот порт, гарантируя тем самым два разных порта.

+0

Но 'findFreePort' открывает сокет и сразу закрывает его, поэтому теоретически снова доступен. – leonbloy

+0

В коде выделен порт, но метод close() отключает этот порт. Затем он может быть повторно использован (например, другой ServerSocket). Итак, я думаю, что другой вызов этого метода может дать тот же номер порта. –

2

Один из способов, чтобы получить два различных номера портов:

ServerSocket socket1= new ServerSocket(0); 
    port1 = socket.getLocalPort(); 
    ServerSocket socket2= new ServerSocket(0); 
    port2 = socket.getLocalPort(); 

    socket1.close(); 
    socket2.close(); 
0

Это так эффективно, как операционная система может сделать это. Однако закрытие ServerSocket сразу же бессмысленно, так как этот порт больше не зарезервирован и может быть выделен для чего-то другого. Единственная цель этого упражнения - создать ServerSocket, чтобы просто создать его.

0

Вот класс, который я использовал для поиска нескольких свободных портов. Он обеспечивает гибкость выделения отдельных портов в потоке некоторой сложной логики (то есть, когда, возможно, количество необходимых вам портов - это не простое число, а зависит от сложной логики). Он по-прежнему гарантирует, что все порты, которые вы запрашиваете бесплатно и уникальны (до тех пор, пока вы используете один и тот же экземпляр этого класса для получения всех портов).

Таким образом, способ использования этого класса заключается в создании экземпляра. Выполните свой код, чтобы сделать то, что вы хотите выделить порты для использования этого экземпляра. Затем, как только вы связали все порты, вы можете избавиться от этого экземпляра и использовать новый в следующий раз.

public class PortFinder { 

/** 
* If you only need the one port you can use this. No need to instantiate the class 
*/ 
public static int findFreePort() throws IOException { 
    ServerSocket socket = new ServerSocket(0); 
    try { 
     return socket.getLocalPort(); 
    } finally { 
     try { 
      socket.close(); 
     } catch (IOException e) { 
     } 
    } 
} 

private HashSet<Integer> used = new HashSet<Integer>(); 

/** 
* Finds a port that is currently free and is guaranteed to be different from any of the 
* port numbers previously returned by this PortFinder instance. 
*/ 
public synchronized int findUniqueFreePort() throws IOException { 
    int port; 
    do { 
     port = findFreePort(); 
    } while (used.contains(port)); 
    used.add(port); 
    return port; 
} 

} 
+0

где находится findFreePort(); способ. @Kris – Roster

+0

Это прямо там, первый член класса PortFinder. Может быть, я смутил вещи необычным порядком размещения его перед полевой декларацией. – Kris