Bagaimana cara menemukan port yang tersedia?

194

Saya ingin memulai server yang mendengarkan port. Saya dapat menentukan port secara eksplisit dan berfungsi. Tetapi saya ingin mencari porta secara otomatis. Dalam hal ini saya punya dua pertanyaan.

  1. Di mana kisaran nomor port yang harus saya cari? (Saya menggunakan port 12345, 12346, dan 12347 dan itu baik-baik saja).

  2. Bagaimana saya bisa mengetahui jika port yang diberikan tidak ditempati oleh perangkat lunak lain?

Roma
sumber

Jawaban:

277

Jika Anda tidak keberatan dengan port yang digunakan, tentukan port 0 ke konstruktor ServerSocket dan port tersebut akan mendengarkan pada port gratis apa pun.

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

Jika Anda ingin menggunakan satu set port tertentu, maka cara termudah mungkin untuk beralih melalui mereka sampai satu berfungsi. Sesuatu seperti ini:

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

Dapat digunakan seperti ini:

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");
}
Graham Edgecombe
sumber
2
Saat menggunakan ServerSocket baru (0), Anda harus berhati-hati untuk menutupnya! Berdasarkan javasourcecode.org/html/open-source/eclipse/eclipse-3.5.2/org/… , sedikit diadaptasi di gist.github.com/3429822
vorburger
9
@vorburger, melakukannya dengan cara itu rentan terhadap kondisi balapan. Lebih baik hanya mendengarkan pada soket server segera, daripada membukanya untuk menemukan port gratis, tutup lagi, dan kemudian buka satu lagi pada port bebas (pada saat ada kemungkinan kecil ada sesuatu yang lain sekarang mendengarkan pada itu port.)
Graham Edgecombe
7
setuju, tapi itu tergantung pada kasus penggunaan yang tepat: Dalam kasus saya, saya perlu menemukan nomor port gratis untuk menyerahkannya ke beberapa API (katakanlah starter Jetty tertanam, untuk tes) - API masing-masing menginginkan nomor soket - bukan yang sudah membuka soket server. Jadi itu tergantung.
vorburger
4
@vorburger API yang Wajar akan menerima nol sebagai nomor port yang valid untuk didengarkan, dan kemudian akan memberi tahu Anda port yang sebenarnya sedang didengarkan. Namun, tidak ada banyak API yang masuk akal: banyak program yang secara khusus menguji 0 port yang dilewati dan menolaknya ( ssh -D 127.0.0.0:0 ...? Tidak!), Yang benar-benar membuat frustrasi. Kami harus menambal sejumlah perpustakaan / program agar dapat digunakan oleh kami.
Joker_vD
2
@vorburger Saat menggunakan port apa pun , harus berhati-hati untuk menutupnya. new ServerSocket(0)tidak berbeda dalam hal ini. Tidak ada apa pun di tautan pertama Anda tentang ini. Tautan kedua Anda hanya menunjukkan praktik buruk. Setelah Anda membangunnya, ServerSocketAnda harus menggunakannya , jangan tutup dan coba gunakan kembali nomor port yang sama. Semua itu hanya buang-buang waktu, dan juga rentan terhadap masalah pengaturan waktu.
Marquis of Lorne
57

Jika Anda meneruskan 0 sebagai nomor port ke konstruktor ServerSocket, Ini akan mengalokasikan port untuk Anda.

Maurice Perry
sumber
Ya, saya pikir itu solusi yang paling elegan tetapi karena beberapa alasan itu tidak berhasil dalam kasus saya. Tetapi saya masih perlu mencari tahu apa sebenarnya yang salah.
Roman
1
@ Roman: Mengapa itu tidak berhasil? Perbarui pertanyaan Anda untuk menyertakan ini (atau orang akan terus menyarankannya) dan jelaskan mengapa solusi ini gagal untuk Anda.
FrustratedWithFormsDesigner
2
Bagaimana cara menemukan port ini dari klien? ;)
ed22
34

Mulai dari Java 1.7 Anda dapat menggunakan coba-dengan-sumber daya seperti ini:

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

    }
  }

Jika Anda perlu menemukan port terbuka pada antarmuka tertentu, periksa dokumentasi ServerSocket untuk konstruktor alternatif.

Peringatan: Kode apa pun yang menggunakan nomor port yang dikembalikan oleh metode ini tunduk pada kondisi balapan - proses / utas yang berbeda dapat mengikat ke port yang sama segera setelah kami menutup instance ServerSocket.

Andrei Savu
sumber
1
Ini mungkin tidak berfungsi jika Anda tidak mengatur SO_REUSEADDR. Dan ada kondisi balapan, tetapi sulit untuk memperbaikinya.
David Ehrmann
Ini mungkin tidak berfungsi jika Anda kemudian mencoba membuka yang lain ServerSocket dengan nomor port yang sama, karena mungkin sudah diambil sementara itu. Mengapa Anda tidak akan mengembalikan yang sebenarnya ServerSocketalih-alih menutupnya tetap menjadi misteri.
Marquis of Lorne
5
@ EJP Terkadang kode pihak ketiga menerima port tetapi bukan soket itu sendiri.
Kapten Man
@ Kapten Man Tentu, tetapi dalam kasus-kasus port pasti diasumsikan dialokasikan sebelumnya. Port yang dialokasikan secara dinamis harus dipasok ke klien oleh beberapa mekanisme lain seperti layanan penamaan atau siaran atau siaran. Seluruh pertanyaan di sini salah.
Marquis of Lorne
@ user207421 Anda tidak dapat mengubah kode pihak ketiga untuk menerima ServerSocketalih - alih intuntuk port.
Kapten Man
30

Menurut Wikipedia , Anda harus menggunakan port 49152untuk 65535jika Anda tidak memerlukan 'terkenal' pelabuhan.

AFAIK satu-satunya cara untuk menentukan apakah suatu port sedang digunakan adalah dengan mencoba membukanya.

Andy Johnson
sumber
6
+1. Karena Wikipedia tidak selalu merupakan sumber kebenaran / fakta absolut, saya pikir mungkin berguna untuk mencatat bahwa, referensi untuk halaman Wikipedia tersebut berasal dari "Otoritas Nomor Penugasan Internet (IANA)" s "[Nama Layanan dan Nomor Port Protokol Transport] Halaman Registry ( iana.org/assignments/service-names-port-numbers/… ", berdasarkan RFC 6335 - Bagian 6 (yaitu referensi" Solid "dalam kasus ini! :)).
OzgurH
25

Jika Anda perlu menggunakan rentang :

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;
    }
}
Serhii Bohutskyi
sumber
bertanya-tanya bagaimana cara memeriksa port, kemudian melepaskannya. Terima kasih, SergeyB!
Vlad Ilie
5
Jika setiap port dalam kisaran [dari, ke) digunakan, kode ini akan berulang tanpa batas (atau setidaknya sampai salah satu port tersebut menjadi bebas). Jika Anda melakukan pemindaian berurutan alih-alih memilih port dalam rentang secara acak, Anda dapat menghindari kemungkinan ini (cukup lemparkan pengecualian ketika Anda sampai ke ujung rentang tanpa menemukan port bebas). Jika Anda benar-benar perlu memilih port secara acak, maka Anda memerlukan set untuk melacak port yang telah Anda coba sejauh ini, dan kemudian meningkatkan kesalahan ketika ukuran set itu sama dengan - dari.
Simon Kissane
Port 0 jauh lebih sederhana dan tidak perlu membuat dua ServerSocketsdalam kasus sukses, dan tidak menderita masalah jendela waktu kode ini.
Marquis of Lorne
11

Eclipse SDK berisi SocketUtil kelas , yang melakukan apa yang Anda inginkan. Anda mungkin melihat kode sumber git .

jopa
sumber
2
Melihat kode Eclipse, mereka melakukan hal yang sama dengan jawaban Graham Edgecombe
KevinL
6

Ini berfungsi untuk saya di Java 6

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

Saya baru-baru ini merilis perpustakaan kecil untuk melakukan hal itu dengan tes dalam pikiran. Ketergantungan maven adalah:

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

Setelah diinstal, nomor port gratis dapat diperoleh melalui:

int port = FreePortFinder.findFreeLocalPort();
Alex Panov
sumber
4

Jika server Anda mulai, maka soket itu tidak digunakan.

EDIT

Sesuatu seperti:

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));
}
OscarRyz
sumber
Tidak tahu siapa yang mengecewakan Anda, tetapi saya mendukung Anda. Anda bisa mengatur fungsi rekursif untuk mencoba men-setup ServerSocket, dan jika Anda mendapatkan IOException (atau apa pun itu), Anda coba lagi sampai berhasil mendapatkan port.
jonescb
Saya pikir lebih baik untuk memeriksa apakah port tersedia dan kemudian mencoba untuk mulai mendengarkan port ini. Tampaknya tidak elegan untuk memulai sesuatu dan kemudian menemukan bahwa ada beberapa masalah dan kemudian mencoba menyelesaikan masalah ini.
Roman
2
@Roman well, ya, itu akan lebih baik, kecuali faktanya tidak ada metode untuk mengetahui apakah port tersedia. Jika new ServerSocket(0)tidak bekerja untuk Anda alternatifnya adalah ini. Saya pikir ada 85% kemungkinan Anda akhirnya menggunakan saran saya.
OscarRyz
Kode ini terus terbuka dan bocor ServerSocketsselamanya dan hanya mempertahankan yang terakhir yang berhasil. Perlu sebuah breakpernyataan.
Marquis of Lorne
4

Jika Anda ingin membuat server sendiri menggunakan a ServerSocket, Anda dapat memilihnya untuk mengambil port gratis untuk Anda:

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

Implementasi server lain biasanya memiliki dukungan serupa. Jetty misalnya memilih port gratis kecuali Anda secara eksplisit mengaturnya:

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

Ini mungkin tidak banyak membantu Anda, tetapi pada mesin (Ubuntu) saya, saya memiliki file / etc / services di mana setidaknya port yang digunakan / disediakan oleh beberapa aplikasi diberikan. Ini adalah port standar untuk aplikasi tersebut.

Tidak ada jaminan bahwa ini sedang berjalan, hanya port default yang digunakan aplikasi ini (jadi Anda tidak boleh menggunakannya jika memungkinkan).

Ada sedikit lebih dari 500 port yang ditentukan, sekitar setengah UDP dan setengah TCP.

File-file dibuat menggunakan informasi oleh IANA, lihat nomor port IANA Ditugaskan .

ekstraneon
sumber
ada daftar serupa (dan lebih lengkap, IIRC) yang datang sebagai bagian dari nmap. +1
rmeador
3

Jika Anda menggunakan Spring, jawaban yang diberikan oleh Michail Nikolaev adalah yang paling sederhana dan terbersih, dan IMO harus diunggulkan. Hanya untuk tujuan kenyamanan, saya akan menambahkan contoh sebaris menggunakan Springframwework SocketUtils .findAvailableTcpPort () metode:

int randomAvailablePort = SocketUtils.findAvailableTcpPort();

Semudah itu, hanya satu baris :). Tentu saja, kelas Utils menawarkan banyak metode menarik lainnya, saya sarankan melihat-lihat dokumen.

Gerardo Roza
sumber
1

Menggunakan kelas 'ServerSocket' kita dapat mengidentifikasi apakah port yang diberikan sedang digunakan atau gratis. ServerSocket menyediakan konstruktor yang mengambil integer (yang merupakan nomor port) sebagai argumen dan menginisialisasi soket server pada port. Jika ServerSocket mengeluarkan IO Exception, maka kami dapat menganggap port ini sudah digunakan.

Cuplikan berikut digunakan untuk mendapatkan semua port yang tersedia.

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

         }
}

Tautan referensi .

Hari Krishna
sumber
0

jika port ditempati oleh perangkat lunak lain kode akan melempar IOException

Cezar Terzian
sumber