Kullanılabilir bir bağlantı noktası nasıl bulunur?


195

Bir bağlantı noktasını dinleyen bir sunucu başlatmak istiyorum. Bağlantı noktasını açıkça belirtebilirim ve işe yarıyor. Ama otomatik olarak bir liman bulmak istiyorum. Bu açıdan iki sorum var.

  1. Hangi port numaralarını aramalıyım? (12345, 12346 ve 12347 portlarını kullandım ve iyiydi).

  2. Belirli bir bağlantı noktasının başka bir yazılım tarafından işgal edilip edilmediğini nasıl öğrenebilirim?

Yanıtlar:


280

Kullanılan bağlantı noktasının sakıncası yoksa, ServerSocket yapıcısına 0 bağlantı noktası belirtin; bu bağlantı noktası herhangi bir boş bağlantı noktasını dinler.

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

Belirli bir bağlantı noktası kümesi kullanmak istiyorsanız, en kolay yol muhtemelen biri çalışana kadar bunları tekrarlamaktır. Bunun gibi bir şey:

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

Şu şekilde kullanılabilir:

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
Yeni ServerSocket (0) kullanırken, kapatmaya dikkat edin! Dayanarak javasourcecode.org/html/open-source/eclipse/eclipse-3.5.2/org/... hafifçe benim de adapte gist.github.com/3429822
vorburger

9
@vorburger, bu şekilde yapmak yarış koşullarına yatkındır. Sunucu soketini hemen dinlemek, daha iyi bir bağlantı noktası bulmak, onu tekrar kapatmak ve daha sonra ücretsiz bağlantı noktasında tekrar açmak daha iyidir (bu sırada başka bir şeyin şu anda dinlemesi için küçük bir şans vardır. bağlantı noktası).
Graham Edgecombe

7
kabul etti, ancak kesin kullanım durumuna bağlı: Benim durumumda, bazı API'lara teslim etmek için ücretsiz bir port numarası bulmam gerekiyordu (testler için gömülü bir Jetty starter diyelim) - ilgili API bir soket numarası istiyor - zaten değil açılan sunucu soketi. Yani değişir.
vorburger

4
@vorburger Makul API'ler, dinlenmek için geçerli bir bağlantı noktası numarası olarak sıfırı kabul eder ve size dinlenen gerçek bağlantı noktasını söyler. Hovewer, pek çok makul API yok: birçok program özellikle 0 portun geçildiğini test ediyor ve reddediyor ( ssh -D 127.0.0.0:0 ...? Hayır!), Bu gerçekten sinir bozucu. Onları bize kullanmak için bir çok kütüphane / programı düzeltmek zorunda kaldık.
Joker_vD

2
@vorburger Herhangi bir bağlantı noktasını kullanırken , kapatılması için dikkatli olunmalıdır. new ServerSocket(0)bu açıdan farklı değil. Bu konuda ilk bağlantınızda hiçbir şey yok. İkinci bağlantınız sadece zayıf uygulama gösterir. İnşa ettiğiniz kez ServerSocketsen gerektiğini kullanmak onu değil, yakın ve de aynı port numarasını yeniden deneyin. Tüm bunlar tam bir zaman kaybıdır ve zamanlama penceresi sorunlarına karşı savunmasızdır.
Lorne Marquis

57

ServerSocket yapıcısına bağlantı noktası numarası olarak 0 iletirseniz, sizin için bir bağlantı noktası atayacaktır.


Evet, bence en zarif çözüm ama bazı nedenlerden dolayı benim durumumda çalışmıyor. Ama yine de tam olarak neyin yanlış olduğunu bulmam gerekiyor.
Roma

1
@Roman: Neden çalışmıyor? Sorunuzu bunu içerecek şekilde güncelleyin (veya kullanıcılar bunu önermeye devam edecektir) ve bu çözümün sizin için neden başarısız olduğunu açıklayın.
FrustratedWithFormsDesigner

2
Bu bağlantı noktasını istemciden nasıl bulurum? ;)
ed22

34

Java 1.7'den başlayarak, aşağıdaki gibi deneme kaynaklarını kullanabilirsiniz:

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

    }
  }

Belirli bir arabirimde açık bir bağlantı noktası bulmanız gerekirse , alternatif kurucular için ServerSocket belgelerine bakın.

Uyarı: Bu yöntemle döndürülen bağlantı noktası numarasını kullanan herhangi bir kod bir yarış durumuna tabidir - ServerSocket örneğini kapattıktan hemen sonra farklı bir işlem / iş parçacığı aynı bağlantı noktasına bağlanabilir.


1
Ayarlamazsanız bu çalışmayabilir SO_REUSEADDR. Ve bir yarış durumu var, ama bunu düzeltmek zor.
David Ehrmann

Daha sonra aynı bağlantı noktası numarasına sahip başka bir ServerSocket tane açmaya çalışırsanız , bu arada alınmış olabilir. Neden ServerSocketkapatmak yerine gerçek olanı geri döndürmüyorsun ?
Lorne Marquis

5
@EJP Bazen üçüncü taraf kodu bir bağlantı noktasını kabul eder, ancak soketin kendisini kabul etmez.
Kaptan Adam

@CaptainMan Elbette, ancak bu durumlarda limanın mutlaka önceden konumlandırıldığı varsayılır. Dinamik olarak ayrılmış bir bağlantı noktası, istemcilere adlandırma hizmeti veya yayın veya çoklu yayın gibi başka bir mekanizma tarafından sağlanmalıdır. Buradaki tüm soru yanlış biçimlendirilmiş.
Lorne Marquis

@ user207421 Bağlantı noktası için bir ServerSocketyerine kabul etmek için üçüncü taraf kodunu değiştiremezsiniz int.
Kaptan Adam

30

Göre Wikipedia , bağlantı noktalarını kullanmalıdır 49152için 65535bir 'iyi bilinen' portunu gerekmiyorsa.

AFAIK, bir limanın ne zaman kullanıldığını belirlemenin tek yolu onu açmaya çalışmaktır.


6
+1. Wikipedia her zaman mutlak gerçeğin / gerçeklerin kaynağı olmadığından, Wikipedia sayfasının referansının "İnternet Atanmış Numaralar Otoritesi (IANA)" s "[Hizmet Adı ve Taşıma Protokolü Bağlantı Noktası Numarası) 'dan geldiğini belirtmek yararlı olabileceğini düşündüm. Kayıt defteri ( iana.org/assignments/service-names-port-numbers/… "sayfası, RFC 6335 - Bölüm 6'ya göre (yani, bu durumda" Katı "başvuru! :)).
OzgurH

25

Kapsamlı kullanım gerekiyorsa :

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

bir limanı nasıl kontrol edeceğimizi merak ediyordum, sonra ondan ayrılıyordu. Teşekkürler SergeyB!
Vlad Ilie

5
[From, to) aralığındaki her bağlantı noktası kullanılıyorsa, bu kod sonsuz döngüde (veya en azından bu bağlantı noktalarından biri serbest kalana kadar) döngü yapar. Aralıktaki bağlantı noktalarını rasgele seçmek yerine sıralı bir tarama yaparsanız, bu olasılığı önleyebilirsiniz (boş bir bağlantı noktası bulmadan aralığın sonuna geldiğinizde bir istisna atın). Bağlantı noktalarını rasgele seçmeniz gerekiyorsa, o zamana kadar denediğiniz bağlantı noktalarını takip etmek için bir kümeye ihtiyacınız vardır ve daha sonra bu kümenin boyutu - değerine eşit olduğunda bir hata yükseltin.
Simon Kissane

Port 0 oldukça basittir ve ServerSocketsbaşarı durumunda iki tane oluşturmayı gerektirmez ve bu kodun zamanlama penceresi sorunlarından muzdarip değildir.
Lorne Marquis




6

Bu benim için Java 6'da çalışıyor

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

6

Kısa bir süre önce, bunu akılda tutarak, küçük bir kütüphane yayınladım. Maven bağımlılığı:

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

Kurulduktan sonra, ücretsiz port numaraları şu yollarla elde edilebilir:

int port = FreePortFinder.findFreeLocalPort();

4

Sunucunuz başlatılırsa, o yuva kullanılmaz.

DÜZENLE

Gibi bir şey:

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

Sana kimin oy verdiğini bilmiyorum, ama ben sana geri oy verdim. ServerSocket'i kurmaya çalışmak için özyinelemeli bir işlev ayarlayabilirsiniz ve bir IOException (veya her ne olursa olsun) alırsanız, bağlantı noktası başarıyla alıncaya kadar yeniden deneyin.
jonescb

Bir portun mevcut olup olmadığını kontrol etmenin ve daha sonra bu portu dinlemeye başlamanın daha iyi olduğunu düşünüyorum. Bir şeye başlamak ve sonra bir sorun olduğunu öğrenmek ve sonra bu sorunları çözmeye çalışmak zarif görünmüyor.
Roman

2
@Roman iyi, evet, daha iyi olurdu, ancak bir limanın mevcut olup olmadığını bilmek için bir yöntem yoktur. Eğer new ServerSocket(0)sizin alternatif çalışmıyorsa budur. Sanırım benim önerimi kullanarak sonuçta% 85 olasılık var.
OscarRyz

Bu kod açma ve sızıntı tutar ServerSocketssonsuza sadece korur son başarılı bir. Bir breakifadeye ihtiyacı var .
Lorne Marquis

4

A kullanarak kendi sunucunuzu oluşturmak ServerSocketistiyorsanız, sadece sizin için ücretsiz bir port seçmesini sağlayabilirsiniz:

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

Diğer sunucu uygulamaları genellikle benzer bir desteğe sahiptir. Örneğin iskelesi , açıkça ayarlamadığınız sürece ücretsiz bir bağlantı noktası seçer:

  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

Size çok yardımcı olmayabilir, ancak (Ubuntu) makinemde, en azından bazı uygulamalar tarafından kullanılan / ayrılmış bağlantı noktalarının verildiği bir dosya / etc / hizmetlerim var. Bunlar, bu uygulamalar için standart bağlantı noktalarıdır.

Bunların çalıştığının garantisi yoktur, sadece bu uygulamaların kullandığı varsayılan bağlantı noktaları (bu nedenle mümkünse kullanmamalısınız).

Tanımlanmış 500'den fazla bağlantı noktası vardır, yaklaşık yarısı UDP ve yarısı TCP.

Dosyalar IANA bilgileri kullanılarak hazırlanır, bkz. IANA Atanan bağlantı noktası numaraları .


nmap'ın bir parçası olarak gelen benzer (ve daha eksiksiz, IIRC) bir liste var. +1
rmeador

3

Spring kullanıyorsanız, Michail Nikolaev'in verdiği cevap en basit ve en temiz olanıdır ve IMO'nun kaldırılması gerekir. Sadece kolaylık olması açısından, Springframwework SocketUtils .findAvailableTcpPort () yöntemini kullanarak satır içi bir örnek ekleyeceğim :

int randomAvailablePort = SocketUtils.findAvailableTcpPort();

Bu kadar kolay, sadece bir satır :). Tabii ki, Utils sınıfı birçok ilginç yöntem sunuyor, belgelere bir göz atmanızı öneririm.


1

'ServerSocket' sınıfını kullanarak verilen portun kullanımda mı yoksa ücretsiz mi olduğunu tespit edebiliriz. ServerSocket bağımsız değişken olarak bir tamsayı (port numarası olan) alan ve port üzerindeki sunucu soketini başlatan bir kurucu sağlar. ServerSocket herhangi bir G / Ç İstisnası atarsa, bu bağlantı noktasının zaten kullanımda olduğunu varsayabiliriz.

Aşağıdaki snippet, kullanılabilir tüm bağlantı noktalarını almak için kullanılır.

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

         }
}

Referans bağlantısı .


0

bağlantı noktasında başka bir yazılım varsa, kod bir IOException kurar

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.