Sockets: Temukan ketersediaan port menggunakan Java

123

Bagaimana cara saya secara terprogram menentukan ketersediaan port di mesin tertentu yang menggunakan Java?

yaitu diberi nomor port, tentukan apakah sudah digunakan atau belum ?.

pengguna54075
sumber
2
Sementara pertanyaan ini adalah tentang bagaimana mengetahui apakah port tertentu sudah digunakan, Anda mungkin telah mendarat di sini mencoba menemukan cara untuk mendapatkan nomor port gratis, yang stackoverflow.com/questions/2675362/… (dengan tautan ke inti saya .github.com / 3429822 ) mencakup lebih baik.
vorburger
Untuk tujuan apa? Jika Anda hanya ingin mencari soket kosong untuk didengarkan, cukup tentukan nol. Teknik pemindaian apa pun bertanggung jawab atas masalah jendela waktu: port bisa kosong saat Anda memindai dan ditempati saat Anda pergi untuk mengklaim masuk.
Marquis dari Lorne

Jawaban:

91

Ini adalah implementasi yang berasal dari proyek unta Apache :

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

Mereka juga memeriksa DatagramSocket untuk memeriksa apakah port tersedia di UDP dan TCP.

Semoga ini membantu.

David Santamaria
sumber
5
Varian yang bagus dari ini jika Anda memiliki MINA adalah AvailablePortFinder.getNextAvailable (1024) yang memberi Anda port non-hak istimewa berikutnya.
Alain O'Dea
9
Namun ini tidak menangkap semuanya. Saya telah digigit oleh nginx yang sedang berjalan di TCP 8000 sementara laporan rutin di atas 8000 tersedia. Tidak tahu mengapa - Saya curiga nginx melakukan beberapa hal licik (ini ada di OS X). Solusi bagi saya adalah melakukan hal di atas dan juga membuka Socket baru ("localhost", port) lalu mengembalikan false jika kita tidak mendapatkan pengecualian. Pikiran?
Mendung Sebagian
2
Masalah yang sama pada Windows mencoba mendeteksi apakah server SMTP potongan kertas sedang berjalan. Solusi di mana Anda membuka koneksi ke port, daripada mencoba mengikatnya (seperti yang disarankan oleh komentar Partly Clodys dan jawaban Ivan) tampaknya bekerja lebih andal.
stian
4
Menarik. Panggilan ke setReuseAddress () sama sekali tidak berguna setelah soket sudah terikat, dan itu juga sepenuhnya bertentangan dengan tujuan kode.
Marquis dari Lorne
3
Kode ini tunduk pada masalah waktu-jendela. Jika tujuannya adalah untuk membuat ServerSocketport listening ke beberapa, itulah yang harus dilakukan, dan mengembalikan ServerSocket. Membuatnya dan kemudian menutupnya dan mengembalikannya memberikan jaminan nol bahwa berikutnya ServerSocketdapat dibuat menggunakan port itu. Ditto for DatagramSocket.
Marquis dari Lorne
41

Untuk Java 7 Anda dapat menggunakan try-with-resource untuk kode yang lebih ringkas:

private static boolean available(int port) {
    try (Socket ignored = new Socket("localhost", port)) {
        return false;
    } catch (IOException ignored) {
        return true;
    }
}
eivindw.dll
sumber
15
Ini tidak menguji apakah di port tersedia. Ini menguji apakah dalam status DENGARKAN, apakah alamat IP dapat dijangkau, dll.
Marquis dari Lorne
1
Selain itu tes ini cukup lambat (hampir satu detik per port).
JMax
bukankah seharusnya itu mengembalikan false ketika IOException tertangkap?
Baroudi Safwen
@BaroudiSafwen Itu sepenuhnya tergantung pada apa pengecualian sebenarnya. Karena ConnectException: 'connection refused', ya itu harus mengembalikan salah. Untuk waktu tunggu, tidak ada yang bisa dikembalikan yang valid, karena jawaban sebenarnya tidak diketahui. Itulah mengapa teknik ini tidak berguna untuk tujuan ini.
Marquis dari Lorne
36

Tampaknya mulai Java 7, jawaban David Santamaria tidak berfungsi lagi. Namun, sepertinya Anda masih dapat menggunakan Socket untuk menguji koneksi.

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);
            }
        }
    }
}
TwentyMiles
sumber
Saya ingin memeriksa apakah server proxy tersedia untuk melakukan panggilan atas namanya.
Dejell
1
Jawaban @ DavidSantamaria tidak pernah berhasil. Tidak ada hubungannya dengan Java 7.
Marquis dari Lorne
35

Jika Anda tidak terlalu peduli dengan kinerja, Anda selalu dapat mencoba mendengarkan di port menggunakan kelas ServerSocket . Jika itu melempar pengecualian kemungkinan itu sedang digunakan.

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

EDIT: Jika semua yang Anda coba lakukan adalah memilih port gratis, maka new ServerSocket(0)akan menemukan satu untuk Anda.

Spencer Ruport
sumber
Gunakan akhirnya untuk pembersihan (coba {...} tangkap {...} akhirnya {if (socket! = Null) socket.close ();}
Hosam Aly
Anda juga dapat mengatur "portTaken = (socket == null);" di akhir alih-alih melakukannya di tangkapan.
Hosam Aly
1
Saya tidak merasa itu termasuk dalam cakupan pertanyaan penulis yang dimaksudkan.
Spencer Ruport
1
Apakah ada cara untuk melakukannya tanpa mencoba / menangkap? Saya tidak keberatan menggunakan setiap port yang tersedia untuk tujuan saya, tetapi iterasi nomor port dan mencoba / catch-ing lebih masing-masing sampai aku menemukan satu gratis adalah boros sedikit. Apakah tidak ada metode 'native boolean available (int)' di suatu tempat, yang hanya memeriksa?
Oren Shalev
27
SeverSocket baru (0) akan secara otomatis memilih port gratis.
Spencer Ruport
10

Solusi berikut ini terinspirasi oleh implementasi SocketUtils dari Spring-core (lisensi Apache).

Dibandingkan dengan solusi lain yang menggunakannya Socket(...)cukup cepat (menguji 1000 port TCP dalam waktu kurang dari satu detik):

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;
    }
}       
JMax
sumber
3

Solusi berbasis soket coba / tangkap, mungkin tidak memberikan hasil yang akurat (alamat soket adalah "localhost" dan dalam beberapa kasus port dapat "ditempati" bukan oleh antarmuka loopback dan setidaknya pada Windows saya telah melihat tes ini gagal yaitu prot secara salah dinyatakan sebagai tersedia).

Ada perpustakaan keren bernama SIGAR , kode berikut dapat menghubungkan Anda:

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;
Shmil The Cat
sumber
Saya mendapatkan: "tidak ada sigar-x86-winnt.dll di java.library.path" Sayangnya itu membutuhkan download dari beberapa dll tambahan yang tidak benar-benar layak di lingkungan perusahaan (saya tidak memiliki kendali atas sistem CI). Sangat buruk .... Terima kasih.
uthomas
@uthomas Saya rasa Anda masih memiliki kendali atas paket penerapan aplikasi Anda, jika demikian, Anda selalu dapat menyediakan DLL / SO runtime asli Sigar di dekat JAR Anda dan menyetel jalur lib JAVA secara pragmatis (melalui peretasan sederhana Anda dapat mempengaruhinya bahkan setelah Anda aplikasi telah diluncurkan oleh JVM).
Shmil The Cat
2

Pembersihan dari jawaban yang ditunjukkan oleh David Santamaria:

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

Ini masih tunduk pada kondisi balapan yang ditunjukkan oleh pengguna207421 di komentar untuk jawaban David Santamaria (sesuatu bisa mengambil port setelah metode ini menutup ServerSocketdan DatagramSocketdan kembali).

Luke Hutchison
sumber
1

Dalam kasus saya, ini membantu untuk mencoba dan menyambung ke port - jika layanan sudah ada, itu akan merespons.

    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);              
    }
Ivan
sumber
1
Bukan apa yang diminta.
Marquis dari Lorne
0

Dalam kasus saya, saya harus menggunakan kelas DatagramSocket.

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

Jangan lupa import dulu

import java.net.DatagramSocket;
import java.net.BindException;
import java.net.SocketException;
kmchmk
sumber
Ini berfungsi untuk port UDP. Pertanyaannya tampaknya tentang port TCP.
Marquis dari Lorne
-2

Saya telah mencoba sesuatu seperti ini dan bekerja sangat baik dengan saya

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

                 }
mayu jadhv
sumber
1
Bukan apa yang diminta.
Marquis dari Lorne
Dan juga soket bocor.
Marquis dari Lorne