Soketler: Java kullanarak bağlantı noktası kullanılabilirliğini keşfedin


123

Java kullanarak belirli bir makinedeki bir bağlantı noktasının kullanılabilirliğini programlı olarak nasıl belirlerim?

yani bir port numarası verildiğinde, halihazırda kullanılıp kullanılmadığını belirleyin.


2
Bu soru, belirli bir bağlantı noktasının halihazırda kullanımda olup olmadığını nasıl bileceğinizle ilgiliyken, buraya stackoverflow.com/questions/2675362/… (benim özümle bağlantıyla) ücretsiz bir bağlantı noktası numarası edinmenin bir yolunu bulmaya çalışmış olabilirsiniz. .github.com / 3429822 ) daha iyi kapsar.
vorburger

Ne amaçla? Dinlemek için sadece boş bir soket bulmak istiyorsanız, sadece sıfır belirtin. Herhangi bir tarama tekniği, zamanlama penceresi sorunlarına yol açar: bağlantı noktası, taradığınızda boş olabilir ve içeri girmeye gittiğinizde meşgul olabilir.
Marquis of Lorne

Yanıtlar:


91

Bu uygulama , Apache gelen deve projesi:

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

Portun UDP ve TCP'de mevcut olup olmadığını kontrol etmek için DatagramSocket'i de kontrol ediyorlar.

Bu yardımcı olur umarım.


5
MINA'nız varsa bunun güzel bir çeşidi, size bir sonraki ayrıcalıklı olmayan bağlantı noktasını sağlayan AvailablePortFinder.getNextAvailable (1024) 'dir.
Alain O'Dea

9
Ancak bu her şeyi yakalayamaz. Yukarıdaki rutin mevcut olarak 8000 rapor ederken, TCP 8000 üzerinde çalışan bir nginx tarafından ısırıldım. Nedeni hakkında hiçbir fikrim yok - nginx'in bazı sinsi şeyler yaptığından şüpheleniyorum (bu OS X'te). Benim için geçici çözüm, yukarıdakileri yapmak ve ayrıca yeni bir Soket ("localhost", bağlantı noktası) açmak ve sonra bir istisna almazsak yanlış döndürmektir. Düşünceler?
Parçalı Bulutlu

2
Windows'ta kağıt kesme SMTP sunucusunun çalışıp çalışmadığını algılamaya çalışan aynı sorun. Bir bağlantı noktasına bağlanmaya çalışmak yerine bir bağlantı noktasına bağlantı açtığınız bir çözüm (Kısmen Clodys yorumunda ve Ivan'ın cevabında önerildiği gibi) daha güvenilir çalışıyor gibi görünüyor.
stian

4
İlginç. Soket zaten bağlandıktan sonra setReuseAddress () çağrısı tamamen anlamsızdır ve aynı zamanda kodun amacına da tamamen aykırıdır.
Lorne Markisi

3
Bu kod, zamanlama penceresi sorunlarına tabidir. Amaç, bir ServerSocketbağlantı noktası için dinleme oluşturmaksa , yapması gereken budur ve ServerSocket. Oluşturmak ve sonra kapatmak ve geri döndürmek, ServerSocketo bağlantı noktası kullanılarak bir sonraki oluşturulabileceğine dair sıfır garanti sağlar . Ditto için DatagramSocket.
Marquis of Lorne

41

Java 7 için, daha derli toplu bir kod için kaynakla dene'yi kullanabilirsiniz:

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

15
Bu, bağlantı noktasında mevcut olup olmadığını test etmez. LISTEN durumunda olup olmadığını, IP adresine ulaşılıp ulaşılamayacağını vb. Test eder.
Marquis of Lorne

1
Ek olarak, bu test oldukça yavaştır (bağlantı noktası başına neredeyse bir saniye).
JMax

bir IOException yakalandığında false döndürmemeli mi?
Baroudi Safwen

@BaroudiSafwen Bu tamamen istisnanın gerçekte ne olduğuna bağlıdır. Çünkü ConnectException: 'connection refused'evet yanlış döndürmelidir. Gerçek cevap bilinmediğinden, zaman aşımları için geri dönebileceği hiçbir şey geçerli olmaz. Bu nedenle bu teknik bu amaç için kullanışsızdır.
Marquis of Lorne

36

Görünüşe göre Java 7'den itibaren David Santamaria'nın cevabı artık güvenilir bir şekilde çalışmıyor. Bununla birlikte, bağlantıyı test etmek için bir Soketi hala güvenilir bir şekilde kullanabileceğiniz görülüyor.

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

Onun adına arama yapmak için bir proxy sunucusunun uygun olup olmadığını kontrol etmek istiyorum.
Dejell

1
@ DavidSantamaria'nın cevabı hiç işe yaramadı. Java 7 ile hiçbir ilgisi yok.
Marquis of Lorne

35

Performansla çok ilgilenmiyorsanız, ServerSocket sınıfını kullanarak her zaman bir bağlantı noktasında dinlemeyi deneyebilirsiniz . Bir istisna atarsa, muhtemelen kullanılıyor demektir.

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

DÜZENLEME: Tek yapmaya çalıştığınız boş bir bağlantı noktası seçmekse new ServerSocket(0), o zaman sizin için bir tane bulacaktır.


Son olarak temizlik için kullanın (son olarak {...} catch {...} deneyin {if (socket! = Null) socket.close ();}
Hosam Aly

Ayrıca "portTaken = (socket == null);" sonunda yakalamaya yapmak yerine.
Hosam Aly

1
Bunun yazarın sorusunun amaçlanan kapsamı içinde olduğunu düşünmedim.
Spencer Ruport

1
Bunu denemeden / yakalamadan yapmanın bir yolu var mı? Amaçlarım için mevcut herhangi bir bağlantı noktasını kullanmaktan çekinmem, ancak bağlantı noktası numaralarını yinelemek ve boş bir tane bulana kadar her birini yakalamaya çalışmak biraz israf olur. Bir yerde, sadece kontrol eden bir 'yerel mantıksal kullanılabilir (int)' yöntemi yok mu?
Oren Shalev

27
yeni SeverSocket (0) otomatik olarak boş bir bağlantı noktası seçecektir.
Spencer Ruport

10

Aşağıdaki çözüm, SocketUtils'ten ilham almıştır Spring- (Apache lisansı) uygulamasından .

Kullanan diğer çözümlerle karşılaştırıldığında Socket(...)oldukça hızlıdır (bir saniyeden daha kısa sürede 1000 TCP bağlantı noktasını test etmek):

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

Try / catch soket tabanlı çözümler doğru sonuçlar vermeyebilir (soket adresi "localhost" ve bazı durumlarda bağlantı noktası geri döngü arayüzü tarafından "işgal edilmiş" olabilir ve en azından Windows'ta bu testin başarısız olduğunu gördüm. protesto yanlış bir şekilde mevcut olarak ilan edildi).

SIGAR adında harika bir kütüphane var, aşağıdaki kod sizi bağlayabilir :

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;

Ben: "java.library.path içinde sigar-x86-winnt.dll yok" Ne yazık ki, kurumsal ortamda gerçekten uygun olmayan bazı ek dll'lerin indirilmesini gerektiriyor (CI sistemi üzerinde hiçbir kontrolüm yok). Çok kötü .... Yine de teşekkürler.
uthomas

@uthomas Uygulama dağıtım paketiniz üzerinde hala kontrole sahip olduğunuzu tahmin ediyorum, bu durumda, JAR'larınızın yakınında her zaman Sigar yerel çalışma zamanı DLL / SO'ları sağlayabilir ve JAVA kitaplık yolunu pragmatik olarak ayarlayabilirsiniz (basit bir saldırı yoluyla bunu, uygulamanızdan sonra bile etkileyebilirsiniz. JVM tarafından başlatıldı).
Shmil The Cat

2

David Santamaria'nın işaret ettiği cevabın temizliği:

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

Bu hala, kullanıcı207421 tarafından David Santamaria'nın cevabına yapılan yorumlarda belirtilen bir yarış durumuna tabidir (bu yöntem ServerSocketve DatagramSocketve geri döndükten sonra bir şey bağlantı noktasını yakalayabilir ).


1

Benim durumumda, bağlantı noktasına bağlanmayı denemeye yardımcı oldu - hizmet zaten mevcutsa, yanıt verecektir.

    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
İstenen değil.
Marquis of Lorne

0

Benim durumumda DatagramSocket sınıfını kullanmak zorunda kaldım.

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

Önce içe aktarmayı unutmayın

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

Bu, UDP bağlantı noktaları için çalışır. Soru TCP bağlantı noktaları ile ilgili gibi görünüyor.
Marquis of Lorne

-2

Bunun gibi bir şey denedim ve bende gerçekten iyi çalıştı

            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
İstenen değil.
Marquis of Lorne

Ve ayrıca bir soket sızıntısı.
Marquis of Lorne
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.