Di localhost, bagaimana cara memilih nomor port gratis?

161

Saya mencoba untuk bermain dengan komunikasi antar-proses dan karena saya tidak tahu cara menggunakan pipa bernama di bawah Windows saya pikir saya akan menggunakan soket jaringan. Semuanya terjadi secara lokal. Server dapat meluncurkan budak dalam proses terpisah dan mendengarkan pada beberapa port. Budak melakukan pekerjaan mereka dan menyerahkan hasilnya kepada tuan. Bagaimana cara mengetahui port mana yang tersedia? Saya menganggap saya tidak dapat mendengarkan pada port 80 atau 21?

Saya menggunakan Python, jika itu memotong pilihan.

Terima kasih!

Anton L.
sumber
1
Kebetulan, jika Anda hanya memilih nomor port acak atau acak-ish (sebaiknya lebih tinggi dari 1024), itu mungkin akan tersedia. Anda bahkan dapat menggunakan port 80 atau 21 atau apa pun, asalkan tidak ada program lain yang mendengarkannya. Pada waktu tertentu, pada sistem normal, hanya sebagian kecil port yang digunakan.
David Z
19
Memilih port acak bukanlah ide yang bagus - biarkan OS memilihnya untuk Anda.
Corehpf
Pada POSIX: stackoverflow.com/questions/913501/…
Ciro Santilli 郝海东 冠状 病 六四 六四 事件

Jawaban:

225

Jangan mengikat ke port tertentu, atau mengikat ke port 0, misalnya sock.bind(('', 0)). OS kemudian akan memilih port yang tersedia untuk Anda. Anda bisa mendapatkan port yang dipilih menggunakan sock.getsockname()[1], dan meneruskannya ke budak sehingga mereka dapat terhubung kembali.

mark4o
sumber
4
Lihat stackoverflow.com/a/2838309/3538289 untuk contohsock.bind(('',0))
cevaris
10
Bagaimana Anda menyampaikan nomor ke budak? Kedengarannya seperti masalah ayam dan telur bagi saya.
Sebastian
2
Jika budak dibuat setelah mengikat, Anda bisa meneruskannya sebagai parameter saat membuatnya. Atau Anda dapat menulisnya ke beberapa memori bersama atau file yang keduanya dapat mengakses, atau server pusat diakses melalui beberapa nomor port terkenal bisa melacaknya.
mark4o
49

Demi cuplikan dari apa yang telah dijelaskan orang-orang di atas:

import socket
from contextlib import closing

def find_free_port():
    with closing(socket.socket(socket.AF_INET, socket.SOCK_STREAM)) as s:
        s.bind(('', 0))
        s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
        return s.getsockname()[1]
saaj
sumber
3
jika di localhost: mungkin s.bind(('localhost', 0))lebih baik
codeskyblue
3
Juga bagus untuk menambahkan yang berikut sehingga Anda dapat dengan cepat menggunakan kembali port itu sebelum pernyataan pengembalian Anda:s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
jonEbird
1
@jonEbird Apakah socket.SO_REUSEADDRbenar-benar membantu dalam kasus ini? Dari apa yang saya baca, hanya relevan bahwa soket yang mencoba untuk mengikat memiliki SO_REUSEADDRdan tidak relevan apakah bendera itu diatur pada soket yang melekat.
Karl Bartel
41

Ikat soket ke port 0. Port bebas acak dari 1024 hingga 65535 akan dipilih. Anda dapat mengambil port yang dipilih dengan getsockname()segera setelah itu bind().

Havenard
sumber
2

Anda dapat mendengarkan port apa pun yang Anda inginkan; umumnya, aplikasi pengguna harus mendengarkan port 1024 ke atas (melalui 65535). Hal utama jika Anda memiliki jumlah pendengar yang bervariasi adalah mengalokasikan rentang ke aplikasi Anda - katakanlah 20000-21000, dan CATCH EXCEPTIONS . Itulah bagaimana Anda akan tahu jika suatu port tidak dapat digunakan (digunakan oleh proses lain, dengan kata lain) di komputer Anda.

Namun, dalam kasus Anda, Anda seharusnya tidak memiliki masalah menggunakan port hard-coded tunggal untuk pendengar Anda, selama Anda mencetak pesan kesalahan jika bind gagal.

Perhatikan juga bahwa sebagian besar soket Anda (untuk para budak) tidak perlu secara eksplisit terikat ke nomor port tertentu - hanya soket yang menunggu koneksi masuk (seperti master Anda di sini) perlu dibuat pendengar dan terikat ke port. Jika port tidak ditentukan untuk soket sebelum digunakan, OS akan menetapkan port yang bisa digunakan untuk soket. Ketika master ingin menanggapi seorang budak yang mengirimkan data, alamat pengirim dapat diakses ketika pendengar menerima data.

Saya kira Anda akan menggunakan UDP untuk ini?

Walt W
sumber
0

Jika Anda hanya perlu menemukan port gratis untuk digunakan nanti, berikut ini cuplikan yang mirip dengan jawaban sebelumnya , tetapi lebih pendek, menggunakan socketserver :

import socketserver

with socketserver.TCPServer(("localhost", 0), None) as s:
    free_port = s.server_address[1]

Perhatikan bahwa port tidak dijamin tetap bebas, jadi Anda mungkin perlu memasukkan cuplikan ini dan kode yang menggunakannya dalam satu lingkaran.

Mihai Capotă
sumber