TCP: dapatkah dua soket berbeda berbagi port?


124

Ini mungkin pertanyaan yang sangat mendasar tetapi membingungkan saya.

Bisakah dua soket terhubung yang berbeda berbagi port? Saya sedang menulis sebuah server aplikasi yang seharusnya dapat menangani lebih dari 100k koneksi bersamaan, dan kami tahu bahwa jumlah port yang tersedia pada sistem adalah sekitar 60k (16bit). Soket yang terhubung ditetapkan ke port baru (khusus), jadi itu berarti jumlah koneksi bersamaan dibatasi oleh jumlah port, kecuali beberapa soket dapat berbagi port yang sama. Jadi pertanyaannya.

Terima kasih atas bantuannya sebelumnya!

Jawaban:


175

Sebuah Server soket mendengarkan pada port tunggal. Semua koneksi klien yang dibuat di server itu terkait dengan port mendengarkan yang sama di sisi server koneksi. Sambungan yang dibuat secara unik diidentifikasi dengan kombinasi pasangan IP / Port sisi klien dan sisi server. Beberapa koneksi pada server yang sama dapat berbagi pasangan IP / Port sisi server yang sama selama mereka terkait dengan pasangan IP / Port sisi klien yang berbeda , dan server akan dapat menangani sebanyak mungkin klien selama sumber daya sistem yang tersedia mengizinkannya. untuk.

Di sisi klien , merupakan praktik umum untuk koneksi keluar baru menggunakan port sisi klien acak , dalam hal ini port yang tersedia mungkin akan habis jika Anda membuat banyak koneksi dalam waktu singkat.


2
Terima kasih atas jawabannya, Remy! Jawaban Anda adalah semua yang saya ingin tahu. ;)
KJ

2
@Remy Connections dibedakan tidak hanya oleh sumber / port tujuan / IP, tetapi juga oleh protokol (TCP, UDP dll.), Jika saya tidak salah.
Ondrej Peterka

1
@OndraPeterka: ya, tetapi tidak semua platform membatasi hal itu. Misalnya, Windows dengan senang hati mengizinkan soket server IPv4 dan IPv6 yang terpisah untuk mendengarkan pada IP lokal yang sama: Port tanpa melewati rintangan, tetapi * sistem Nix (termasuk Linux dan Android) tidak.
Remy Lebeau

6
@ user2268997: Anda tidak dapat menggunakan satu soket untuk menyambung ke beberapa server. Anda harus membuat soket terpisah untuk setiap koneksi.
Remy Lebeau

3
@FernandoGonzalezSanchez: Satu klien dapat memiliki beberapa soket TCP yang terikat ke pasangan IP / Port lokal yang sama selama mereka terhubung ke pasangan IP / Port jarak jauh yang berbeda. Itu tidak khusus untuk Windows, itu adalah bagian dari cara kerja TCP secara umum.
Remy Lebeau

182

TCP / HTTP Mendengarkan Pada Port: Bagaimana Banyak Pengguna Dapat Berbagi Port Yang Sama

Jadi, apa yang terjadi ketika server mendengarkan koneksi masuk pada port TCP? Misalnya, Anda memiliki server web pada port 80. Anggaplah komputer Anda memiliki alamat IP publik 24.14.181.229 dan orang yang mencoba menyambung ke Anda memiliki alamat IP 10.1.2.3. Orang ini dapat terhubung dengan Anda dengan membuka soket TCP ke 24.14.181.229:80. Cukup sederhana.

Secara intuitif (dan salah), kebanyakan orang berasumsi bahwa tampilannya seperti ini:

    Local Computer    | Remote Computer
    --------------------------------
    <local_ip>:80     | <foreign_ip>:80

    ^^ not actually what happens, but this is the conceptual model a lot of people have in mind.

Ini intuitif, karena dari sudut pandang klien, dia memiliki alamat IP, dan terhubung ke server di IP: PORT. Karena klien terhubung ke port 80, maka portnya juga harus 80? Ini adalah hal yang masuk akal untuk dipikirkan, tetapi sebenarnya bukan apa yang terjadi. Jika itu benar, kami hanya dapat melayani satu pengguna per alamat IP asing. Setelah komputer jarak jauh terhubung, maka dia akan memonopoli koneksi port 80 ke port 80, dan tidak ada orang lain yang dapat terhubung.

Tiga hal yang harus dipahami:

1.) Di server, proses sedang mendengarkan di port. Setelah mendapat koneksi, itu menyerahkannya ke utas lain. Komunikasi tidak pernah membebani port mendengarkan.

2.) Koneksi dikenali secara unik oleh OS dengan 5-tuple berikut: (local-IP, local-port, remote-IP, remote-port, protocol). Jika ada elemen dalam tupel yang berbeda, maka ini adalah koneksi yang sepenuhnya independen.

3.) Ketika klien terhubung ke server, ia mengambil port sumber tingkat tinggi acak yang tidak terpakai . Dengan cara ini, satu klien dapat memiliki hingga ~ 64k koneksi ke server untuk port tujuan yang sama.

Jadi, inilah yang sebenarnya dibuat ketika klien terhubung ke server:

    Local Computer   | Remote Computer           | Role
    -----------------------------------------------------------
    0.0.0.0:80       | <none>                    | LISTENING
    127.0.0.1:80     | 10.1.2.3:<random_port>    | ESTABLISHED

Melihat Apa yang Sebenarnya Terjadi

Pertama, mari gunakan netstat untuk melihat apa yang terjadi di komputer ini. Kami akan menggunakan port 500, bukan 80 (karena banyak hal terjadi pada port 80 karena ini adalah port yang umum, tetapi secara fungsional tidak membuat perbedaan).

    netstat -atnp | grep -i ":500 "

Seperti yang diharapkan, hasilnya kosong. Sekarang mari kita mulai server web:

    sudo python3 -m http.server 500

Sekarang, inilah hasil dari menjalankan netstat lagi:

    Proto Recv-Q Send-Q Local Address           Foreign Address         State  
    tcp        0      0 0.0.0.0:500             0.0.0.0:*               LISTEN      - 

Jadi sekarang ada satu proses yang aktif mendengarkan (Status: LISTEN) pada port 500. Alamat lokalnya adalah 0.0.0.0, yang merupakan kode untuk "mendengarkan semua alamat ip". Kesalahan yang mudah dilakukan adalah hanya mendengarkan pada port 127.0.0.1, yang hanya akan menerima koneksi dari komputer saat ini. Jadi ini bukan koneksi, ini hanya berarti bahwa proses diminta untuk mengikat () ke port IP, dan proses tersebut bertanggung jawab untuk menangani semua koneksi ke port tersebut. Ini mengisyaratkan batasan bahwa hanya ada satu proses per komputer yang mendengarkan pada port (ada cara untuk menyiasatinya menggunakan multiplexing, tetapi ini adalah topik yang jauh lebih rumit). Jika server web mendengarkan pada port 80, itu tidak dapat berbagi port itu dengan server web lain.

Jadi sekarang, mari hubungkan pengguna ke mesin kita:

    quicknet -m tcp -t localhost:500 -p Test payload.

Ini adalah skrip sederhana ( https://github.com/grokit/quickweb ) yang membuka soket TCP, mengirim payload ("Uji payload". Dalam hal ini), menunggu beberapa detik dan memutuskan hubungan. Melakukan netstat lagi saat ini terjadi akan menampilkan berikut ini:

    Proto Recv-Q Send-Q Local Address           Foreign Address         State  
    tcp        0      0 0.0.0.0:500             0.0.0.0:*               LISTEN      -
    tcp        0      0 192.168.1.10:500        192.168.1.13:54240      ESTABLISHED -

Jika Anda terhubung dengan klien lain dan melakukan netstat lagi, Anda akan melihat yang berikut ini:

    Proto Recv-Q Send-Q Local Address           Foreign Address         State  
    tcp        0      0 0.0.0.0:500             0.0.0.0:*               LISTEN      -
    tcp        0      0 192.168.1.10:500        192.168.1.13:26813      ESTABLISHED -

... yaitu, klien menggunakan port acak lain untuk koneksi tersebut. Jadi tidak pernah ada kebingungan antara alamat IP.


11
Ini adalah jawaban terbaik yang pernah saya lihat di SO.
Pekerjaan

1
@ N0thing "Dengan cara ini, satu klien dapat memiliki hingga ~ 64k koneksi ke server untuk port tujuan yang sama." Jadi dalam praktiknya, jika klien tidak terhubung ke server dan port yang sama, dua kali atau beberapa kali secara bersamaan, maka klien dapat memiliki lebih dari ~ 64K koneksi. Benarkah itu. Jika ya, maka itu berarti bahwa dari satu port di sisi klien, ia dapat memiliki koneksi ke banyak proses server yang berbeda (seperti koneksi soket berbeda). Jadi secara keseluruhan, beberapa soket klien dapat berada di port yang sama pada mesin klien? Silakan baca komentar saya untuk jawaban "Remey Lebeau". Terima kasih: D
Prem KTiw

6
@premktiw: Ya, beberapa soket klien dapat diikat ke pasangan IP / port lokal yang sama pada saat yang sama, jika mereka terhubung ke pasangan IP / Port server yang berbeda sehingga tupel pasangan lokal + jarak jauh unik. Dan ya, ada kemungkinan bagi klien untuk memiliki lebih dari 64K total koneksi bersamaan. Dari satu port, itu dapat dihubungkan ke server yang berpotensi tidak terbatas (dibatasi oleh sumber daya OS yang tersedia, port router yang tersedia, dll) selama pasangan IP / port server unik.
Remy Lebeau

1
@RemyLebe Puas. Terima kasih banyak: D
Prem KTiw

1
@bibstha Bagaimana firewall menangani port acak, ketika semua koneksi masuk menolak?
PatrykG

35

Soket yang terhubung ditetapkan ke port (khusus) baru

Itu intuisi yang umum, tapi itu tidak benar. Soket yang terhubung tidak ditetapkan ke port baru / khusus. Satu-satunya kendala sebenarnya yang harus dipenuhi oleh tumpukan TCP adalah bahwa tupel (local_address, local_port, remote_address, remote_port) harus unik untuk setiap koneksi soket. Dengan demikian server dapat memiliki banyak soket TCP menggunakan porta lokal yang sama, selama masing-masing soket pada port tersebut terhubung ke lokasi jarak jauh yang berbeda.

Lihat paragraf "Pasangan Soket" di: http://books.google.com/books?id=ptSC4LpwGA0C&lpg=PA52&dq=socket%20pair%20tuple&pg=PA52#v=onepage&q=socket%20pair%20tuple&f=false


1
Terima kasih atas jawaban yang sempurna, Jeremy!
KJ

6
Apa yang Anda katakan sepenuhnya benar dari sisi server. Namun struktur BSD Sockets API berarti bahwa port sisi klien yang keluar harus unik dalam praktiknya, karena bind()operasi tersebut mendahului connect()operasi, bahkan secara implisit.
Marquis dari Lorne

1
@EJP Hai, saya pikir sebelumnya bind()hanya digunakan di sisi server accept()?Jadi sisi klien akan mengikat port tertentu juga?
GMsoF

5
@GMsoF: bind()dapat digunakan di sisi klien sebelumnya connect().
Remy Lebeau

10

Secara teoritis, ya. Berlatih, tidak. Kebanyakan kernel (termasuk linux) tidak mengizinkan Anda sedetik punbind() ke port yang telah dialokasikan. Itu bukan tambalan yang sangat besar untuk membuat ini diizinkan.

Secara konseptual, kita harus membedakan antara soket dan port . Soket adalah titik akhir komunikasi dua arah, yaitu "benda" di mana kita dapat mengirim dan menerima byte. Ini adalah hal yang konseptual, tidak ada bidang seperti itu di header paket bernama "socket".

Port adalah pengenal yang mampu mengidentifikasi soket. Dalam kasus TCP, port adalah 16 bit integer, tetapi ada protokol lain juga (misalnya, pada soket unix, "port" pada dasarnya adalah string).

Masalah utamanya adalah sebagai berikut: jika paket masuk tiba, kernel dapat mengidentifikasi soketnya dengan nomor port tujuannya. Ini adalah cara yang paling umum, tetapi ini bukan satu-satunya kemungkinan:

  • Soket dapat diidentifikasi dengan IP tujuan dari paket masuk. Ini adalah kasusnya, misalnya, jika kita memiliki server yang menggunakan dua IP secara bersamaan. Kemudian kita dapat menjalankan, misalnya, server web yang berbeda pada port yang sama, tetapi pada IP yang berbeda.
  • Soket dapat dikenali dari port sumber dan ipnya juga. Ini adalah kasus di banyak konfigurasi load balancing.

Karena Anda bekerja pada server aplikasi, itu akan dapat melakukan itu.


2
Dia tidak bertanya tentang membuat sedetik bind().
Marquis dari Lorne

1
@ user207421 Apakah Anda pernah melihat OS yang tidak memasang soket pendengar bind()? Saya dapat membayangkannya, ya itu sangat mungkin, tetapi kenyataannya adalah bahwa WinSock dan API Posix menggunakan bind()panggilan untuk itu, bahkan parametrisasinya secara praktis sama. Bahkan jika API tidak memiliki panggilan ini, bagaimanapun Anda perlu mengatakannya, dari mana Anda ingin membaca byte yang masuk .
peterh - Pulihkan Monica

1
@ user207421 Tentu saja 100k atau lebih koneksi TCP dapat ditangani dengan port yang sama, listen()/ accept()panggilan API dapat membuat soket dengan cara yang akan dibedakan oleh kernel dengan port masuknya. Pertanyaan tentang OP dapat diinterpretasikan dari cara yang dia minta pada dasarnya. Saya pikir, ini cukup realistis, tetapi bukan ini yang dimaksud dengan pertanyaannya secara harfiah.
peterh - Pulihkan Monica

1

Tidak. Tidak mungkin berbagi port yang sama pada saat tertentu. Tetapi Anda dapat membuat aplikasi Anda sedemikian rupa sehingga akan membuat akses port pada saat yang berbeda.

Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.