GNU/Linux >> Belajar Linux >  >> Linux

Apa perbedaan SO_REUSEADDR dan SO_REUSEPORT?

Selamat datang di dunia portabilitas yang indah... atau lebih tepatnya kekurangannya. Sebelum kita mulai menganalisis kedua opsi ini secara mendetail dan melihat lebih dalam bagaimana sistem operasi yang berbeda menanganinya, perlu dicatat bahwa implementasi soket BSD adalah ibu dari semua implementasi soket. Pada dasarnya semua sistem lain menyalin implementasi soket BSD pada suatu saat (atau setidaknya antarmukanya) dan kemudian mulai mengembangkannya sendiri. Tentu saja implementasi soket BSD juga berevolusi pada saat yang sama dan dengan demikian sistem yang menyalinnya kemudian mendapatkan fitur yang kurang dari sistem yang menyalinnya sebelumnya. Memahami implementasi soket BSD adalah kunci untuk memahami semua implementasi soket lainnya, jadi Anda harus membacanya bahkan jika Anda tidak ingin menulis kode untuk sistem BSD.

Ada beberapa dasar yang harus Anda ketahui sebelum kita melihat kedua opsi ini. Koneksi TCP/UDP diidentifikasi oleh tuple yang terdiri dari lima nilai:

{<protocol>, <src addr>, <src port>, <dest addr>, <dest port>}

Setiap kombinasi unik dari nilai-nilai ini mengidentifikasi koneksi. Akibatnya, tidak ada dua koneksi yang dapat memiliki lima nilai yang sama, jika tidak, sistem tidak akan dapat lagi membedakan koneksi ini.

Protokol soket diatur saat soket dibuat dengan socket() fungsi. Alamat sumber dan port diatur dengan bind() fungsi. Alamat dan port tujuan diatur dengan connect() fungsi. Karena UDP adalah protokol tanpa koneksi, soket UDP dapat digunakan tanpa menghubungkannya. Namun diperbolehkan untuk menghubungkannya dan dalam beberapa kasus sangat menguntungkan untuk kode Anda dan desain aplikasi umum. Dalam mode tanpa koneksi, soket UDP yang tidak terikat secara eksplisit ketika data dikirim melaluinya untuk pertama kali biasanya secara otomatis terikat oleh sistem, karena soket UDP yang tidak terikat tidak dapat menerima data (balasan) apa pun. Hal yang sama berlaku untuk soket TCP yang tidak terikat, ia terikat secara otomatis sebelum dihubungkan.

Jika Anda secara eksplisit mengikat soket, dimungkinkan untuk mengikatnya ke port 0 , yang berarti "port apa saja". Karena soket tidak dapat benar-benar terikat ke semua port yang ada, sistem harus memilih port tertentu itu sendiri dalam kasus itu (biasanya dari rentang port sumber khusus OS yang telah ditentukan sebelumnya). Wildcard serupa ada untuk alamat sumber, yang bisa berupa "alamat apa saja" (0.0.0.0 dalam hal IPv4 dan :: dalam kasus IPv6). Tidak seperti port, soket benar-benar dapat diikat ke "alamat apa pun" yang berarti "semua alamat IP sumber dari semua antarmuka lokal". Jika nanti soket dihubungkan, sistem harus memilih alamat IP sumber tertentu, karena soket tidak dapat dihubungkan dan pada saat yang sama terikat ke alamat IP lokal mana pun. Bergantung pada alamat tujuan dan konten tabel perutean, sistem akan memilih alamat sumber yang sesuai dan mengganti pengikatan "apa saja" dengan pengikatan ke alamat IP sumber yang dipilih.

Secara default, tidak ada dua soket yang dapat diikat ke kombinasi alamat sumber dan port sumber yang sama. Selama port sumbernya berbeda, alamat sumber sebenarnya tidak relevan. Mengikat socketA ke ipA:portA dan socketB ke ipB:portB selalu memungkinkan jika ipA != ipB berlaku, bahkan ketika portA == portB . Misalnya. socketA milik program server FTP dan terikat ke 192.168.0.1:21 dan socketB milik program server FTP lain dan terikat ke 10.0.0.1:21 , kedua binding akan berhasil. Perlu diingat, bahwa soket mungkin terikat secara lokal ke "alamat apa pun". Jika soket terikat ke 0.0.0.0:21 , itu terikat ke semua alamat lokal yang ada pada saat yang sama dan dalam hal ini tidak ada soket lain yang dapat diikat ke port 21 , terlepas dari alamat IP spesifik mana yang coba diikat, sebagai 0.0.0.0 bentrok dengan semua alamat IP lokal yang ada.

Apa pun yang dikatakan sejauh ini hampir sama untuk semua sistem operasi utama. Hal-hal mulai menjadi spesifik untuk OS ketika penggunaan kembali alamat ikut berperan. Kita mulai dengan BSD, karena seperti yang saya katakan di atas, ini adalah induk dari semua implementasi soket.

BSD

SO_REUSEADDR

Jika SO_REUSEADDR diaktifkan pada soket sebelum mengikatnya, soket dapat berhasil diikat kecuali ada konflik dengan soket lain yang terikat persis kombinasi yang sama dari alamat sumber dan port. Sekarang Anda mungkin bertanya-tanya bagaimana perbedaannya dari sebelumnya? Kata kuncinya adalah "tepat". SO_REUSEADDR terutama mengubah cara alamat karakter pengganti ("alamat IP apa pun") diperlakukan saat mencari konflik.

Tanpa SO_REUSEADDR , mengikat socketA ke 0.0.0.0:21 dan kemudian mengikat socketB ke 192.168.0.1:21 akan gagal (dengan kesalahan EADDRINUSE ), karena 0.0.0.0 berarti "alamat IP lokal apa pun", sehingga semua alamat IP lokal dianggap digunakan oleh soket ini dan ini termasuk 192.168.0.1 , juga. Dengan SO_REUSEADDR itu akan berhasil, sejak 0.0.0.0 dan 192.168.0.1 adalah tidak persis alamat yang sama, satu adalah wildcard untuk semua alamat lokal dan yang lainnya adalah alamat lokal yang sangat spesifik. Perhatikan bahwa pernyataan di atas benar terlepas dari urutan socketA dan socketB yang terikat; tanpa SO_REUSEADDR itu akan selalu gagal, dengan SO_REUSEADDR itu akan selalu berhasil.

Untuk memberi Anda ikhtisar yang lebih baik, mari buat tabel di sini dan buat daftar semua kemungkinan kombinasi:

SO_REUSEADDR       socketA        socketB       Result
---------------------------------------------------------------------
  ON/OFF       192.168.0.1:21   192.168.0.1:21    Error (EADDRINUSE)
  ON/OFF       192.168.0.1:21      10.0.0.1:21    OK
  ON/OFF          10.0.0.1:21   192.168.0.1:21    OK
   OFF             0.0.0.0:21   192.168.1.0:21    Error (EADDRINUSE)
   OFF         192.168.1.0:21       0.0.0.0:21    Error (EADDRINUSE)
   ON              0.0.0.0:21   192.168.1.0:21    OK
   ON          192.168.1.0:21       0.0.0.0:21    OK
  ON/OFF           0.0.0.0:21       0.0.0.0:21    Error (EADDRINUSE)

Tabel di atas mengasumsikan bahwa socketA telah berhasil diikat ke alamat yang diberikan untuk socketA , lalu socketB dibuat, atau mendapatkan SO_REUSEADDR disetel atau tidak, dan akhirnya terikat ke alamat yang diberikan untuk socketB . Result adalah hasil dari operasi pengikatan untuk socketB . Jika kolom pertama bertuliskan ON/OFF , nilai SO_REUSEADDR tidak relevan dengan hasilnya.

Oke, SO_REUSEADDR berpengaruh pada alamat wildcard, baik untuk diketahui. Namun itu bukan satu-satunya efek yang dimilikinya. Ada efek terkenal lainnya yang juga menjadi alasan mengapa kebanyakan orang menggunakan SO_REUSEADDR dalam program server di tempat pertama. Untuk penggunaan penting lainnya dari opsi ini, kita harus melihat lebih dalam tentang cara kerja protokol TCP.

Jika soket TCP sedang ditutup, biasanya dilakukan jabat tangan 3 arah; urutannya disebut FIN-ACK . Masalahnya di sini adalah, bahwa ACK terakhir dari urutan itu mungkin telah tiba di sisi lain atau mungkin belum tiba dan hanya jika sudah, sisi lain juga menganggap soket tertutup sepenuhnya. Untuk mencegah penggunaan kembali kombinasi alamat+port, yang mungkin masih dianggap terbuka oleh beberapa peer jarak jauh, sistem tidak akan segera menganggap soket mati setelah mengirim ACK terakhir tetapi alih-alih menempatkan soket ke keadaan yang biasa disebut sebagai TIME_WAIT . Itu bisa dalam keadaan itu selama beberapa menit (pengaturan tergantung sistem). Pada sebagian besar sistem, Anda dapat menyiasati keadaan tersebut dengan mengaktifkan penundaan dan menyetel waktu penundaan nol1 tetapi tidak ada jaminan bahwa ini selalu memungkinkan, bahwa sistem akan selalu memenuhi permintaan ini, dan bahkan jika sistem menghormatinya, hal ini menyebabkan socket untuk ditutup dengan reset (RST ), yang tidak selalu merupakan ide bagus. Untuk mempelajari lebih lanjut tentang waktu berlama-lama, lihat jawaban saya tentang topik ini.

Pertanyaannya adalah, bagaimana sistem memperlakukan soket dalam status TIME_WAIT ? Jika SO_REUSEADDR tidak disetel, soket dalam status TIME_WAIT dianggap masih terikat ke alamat sumber dan port dan setiap upaya untuk mengikat soket baru ke alamat dan port yang sama akan gagal sampai soket benar-benar telah ditutup. Jadi jangan berharap Anda dapat mengubah alamat sumber soket segera setelah menutupnya. Dalam kebanyakan kasus ini akan gagal. Namun, jika SO_REUSEADDR disetel untuk soket yang Anda coba ikat, soket lain terikat ke alamat dan port yang sama dalam status TIME_WAIT diabaikan begitu saja, lagipula sudah "setengah mati", dan soket Anda dapat mengikat ke alamat yang persis sama tanpa masalah. Dalam hal ini tidak ada peran bahwa soket lain mungkin memiliki alamat dan port yang persis sama. Perhatikan bahwa ikat soket ke alamat dan port yang persis sama dengan soket sekarat di TIME_WAIT keadaan dapat memiliki efek samping yang tidak terduga, dan biasanya tidak diinginkan, jika soket lain masih "bekerja", tetapi itu di luar cakupan jawaban ini dan untungnya efek samping tersebut agak jarang dalam praktiknya.

Ada satu hal terakhir yang harus Anda ketahui tentang SO_REUSEADDR . Semua yang tertulis di atas akan berfungsi selama soket yang ingin Anda ikat mengaktifkan penggunaan ulang alamat. Tidak perlu soket lain, yang sudah diikat atau ada di dalam TIME_WAIT negara bagian, juga menyetel bendera ini saat terikat. Kode yang memutuskan apakah pengikatan akan berhasil atau gagal hanya memeriksa SO_REUSEADDR bendera soket dimasukkan ke dalam bind() panggilan, untuk semua soket lain yang diperiksa, bendera ini bahkan tidak dilihat.

SO_PENGGUNAAN KEMBALI

SO_REUSEPORT adalah apa yang diharapkan kebanyakan orang SO_REUSEADDR menjadi. Pada dasarnya, SO_REUSEPORT memungkinkan Anda untuk mengikat sembarang jumlah soket ke persis alamat sumber dan port yang sama selama semua soket terikat sebelumnya juga memiliki SO_REUSEPORT ditetapkan sebelum mereka diikat. Jika soket pertama yang terikat ke alamat dan port tidak memiliki SO_REUSEPORT diatur, tidak ada soket lain yang dapat diikat ke alamat dan port yang persis sama, terlepas dari apakah soket lain ini memiliki SO_REUSEPORT disetel atau tidak, hingga soket pertama melepaskan ikatannya lagi. Berbeda dengan SO_REUESADDR kode yang menangani SO_REUSEPORT tidak hanya akan memverifikasi bahwa soket yang terikat saat ini memiliki SO_REUSEPORT set tetapi juga akan memverifikasi bahwa soket dengan alamat dan port yang bertentangan memiliki SO_REUSEPORT atur saat terikat.

SO_REUSEPORT tidak menyiratkan SO_REUSEADDR . Ini berarti jika soket tidak memiliki SO_REUSEPORT atur kapan terikat dan soket lain memiliki SO_REUSEPORT diatur ketika terikat ke alamat dan port yang persis sama, pengikatan gagal, yang diharapkan, tetapi juga gagal jika soket lain sudah mati dan berada di TIME_WAIT negara. Untuk dapat mengikat soket ke alamat dan port yang sama dengan soket lain di TIME_WAIT negara membutuhkan SO_REUSEADDR untuk disetel pada soket itu atau SO_REUSEPORT harus sudah disetel pada keduanya soket sebelum mengikatnya. Tentu saja diperbolehkan untuk menyetel keduanya, SO_REUSEPORT dan SO_REUSEADDR , pada soket.

Tidak banyak yang bisa dikatakan tentang SO_REUSEPORT selain itu ditambahkan setelah SO_REUSEADDR , itu sebabnya Anda tidak akan menemukannya di banyak implementasi soket dari sistem lain, yang "mempercabangkan" kode BSD sebelum opsi ini ditambahkan, dan bahwa tidak ada cara untuk mengikat dua soket ke alamat soket yang persis sama di BSD sebelum ini opsi.

Connect() Mengembalikan EADDRINUSE?

Kebanyakan orang tahu bahwa bind() mungkin gagal dengan kesalahan EADDRINUSE , namun, ketika Anda mulai bermain-main dengan penggunaan kembali alamat, Anda mungkin mengalami situasi aneh bahwa connect() gagal dengan kesalahan itu juga. Bagaimana ini bisa terjadi? Bagaimana alamat jarak jauh, setelah semua yang terhubung ditambahkan ke soket, sudah digunakan? Menghubungkan beberapa soket ke alamat jarak jauh yang persis sama tidak pernah menjadi masalah sebelumnya, jadi apa yang salah di sini?

Seperti yang saya katakan di bagian paling atas dari balasan saya, sebuah koneksi ditentukan oleh tuple yang terdiri dari lima nilai, ingat? Dan saya juga mengatakan, bahwa kelima nilai ini harus unik jika tidak sistem tidak dapat lagi membedakan dua koneksi, bukan? Nah, dengan penggunaan kembali alamat, Anda dapat mengikat dua soket dari protokol yang sama ke alamat dan port sumber yang sama. Itu berarti tiga dari lima nilai tersebut sudah sama untuk kedua soket ini. Jika sekarang Anda mencoba menghubungkan kedua soket ini juga ke alamat dan port tujuan yang sama, Anda akan membuat dua soket yang terhubung, yang tupelnya benar-benar identik. Ini tidak dapat berfungsi, setidaknya tidak untuk koneksi TCP (koneksi UDP bukanlah koneksi yang sebenarnya). Jika data tiba untuk salah satu dari dua koneksi, sistem tidak dapat mengetahui koneksi mana yang menjadi milik data tersebut. Setidaknya alamat tujuan atau port tujuan harus berbeda untuk salah satu koneksi, sehingga sistem tidak memiliki masalah untuk mengidentifikasi milik koneksi mana data yang masuk.

Jadi jika Anda mengikat dua soket dari protokol yang sama ke alamat sumber dan port yang sama dan mencoba menghubungkan keduanya ke alamat dan port tujuan yang sama, connect() sebenarnya akan gagal dengan kesalahan EADDRINUSE untuk soket kedua yang Anda coba sambungkan, yang berarti soket dengan tupel identik dengan lima nilai sudah tersambung.

Alamat Multicast

Kebanyakan orang mengabaikan fakta bahwa alamat multicast ada, tetapi memang ada. Sementara alamat unicast digunakan untuk komunikasi satu-ke-satu, alamat multicast digunakan untuk komunikasi satu-ke-banyak. Kebanyakan orang mengetahui alamat multicast ketika mereka mempelajari tentang IPv6 tetapi alamat multicast juga ada di IPv4, meskipun fitur ini tidak pernah digunakan secara luas di Internet publik.

Arti dari SO_REUSEADDR perubahan untuk alamat multicast karena memungkinkan banyak soket terikat ke kombinasi alamat dan port multicast sumber yang persis sama. Dengan kata lain, untuk alamat multicast SO_REUSEADDR berperilaku persis seperti SO_REUSEPORT untuk alamat unicast. Sebenarnya, kode memperlakukan SO_REUSEADDR dan SO_REUSEPORT identik untuk alamat multicast, itu berarti Anda bisa mengatakan bahwa SO_REUSEADDR menyiratkan SO_REUSEPORT untuk semua alamat multicast dan sebaliknya.


FreeBSD/OpenBSD/NetBSD

Semua ini adalah garpu yang agak terlambat dari kode BSD asli, itu sebabnya ketiganya menawarkan opsi yang sama seperti BSD dan mereka juga berperilaku dengan cara yang sama seperti di BSD.


macOS (MacOS X)

Pada intinya, macOS hanyalah UNIX bergaya BSD bernama "Darwin ", berdasarkan garpu kode BSD yang agak terlambat (BSD 4.3), yang kemudian bahkan disinkronkan ulang dengan basis kode FreeBSD 5 (pada saat itu saat ini) untuk rilis Mac OS 10.3, sehingga Apple dapat memperoleh kepatuhan penuh POSIX (macOS bersertifikat POSIX). Meskipun memiliki mikrokernel pada intinya ("Mach "), sisa kernel ("XNU ") pada dasarnya hanyalah sebuah kernel BSD, dan itulah mengapa macOS menawarkan opsi yang sama seperti BSD dan mereka juga berperilaku dengan cara yang sama seperti di BSD.

iOS / watchOS / tvOS

iOS hanyalah garpu macOS dengan kernel yang sedikit dimodifikasi dan dipangkas, perangkat ruang pengguna yang agak dipreteli, dan kerangka kerja default yang sedikit berbeda. watchOS dan tvOS adalah garpu iOS, yang dipreteli lebih jauh (terutama watchOS). Sepengetahuan saya, mereka semua berperilaku persis seperti macOS.


Linux

Linux <3.9

Sebelum Linux 3.9, hanya opsi SO_REUSEADDR ada. Opsi ini secara umum berperilaku sama seperti di BSD dengan dua pengecualian penting:

  1. Selama soket TCP pendengar (server) terikat ke port tertentu, SO_REUSEADDR opsi sepenuhnya diabaikan untuk semua soket yang menargetkan port itu. Mengikat soket kedua ke port yang sama hanya dimungkinkan jika dimungkinkan di BSD tanpa memiliki SO_REUSEADDR mengatur. Misalnya. Anda tidak dapat mengikat ke alamat wildcard dan kemudian ke alamat yang lebih spesifik atau sebaliknya, keduanya dimungkinkan di BSD jika Anda menyetel SO_REUSEADDR . Apa yang dapat Anda lakukan adalah Anda dapat mengikat ke port yang sama dan dua alamat non-karakter pengganti yang berbeda, seperti yang selalu diizinkan. Dalam aspek ini Linux lebih ketat daripada BSD.

  2. Pengecualian kedua adalah untuk soket klien, opsi ini berperilaku persis seperti SO_REUSEPORT di BSD, selama keduanya memasang bendera ini sebelum diikat. Alasan untuk mengizinkannya adalah karena penting untuk dapat mengikat banyak soket ke alamat soket UDP yang sama untuk berbagai protokol dan karena dulu tidak ada SO_REUSEPORT sebelum 3.9, perilaku SO_REUSEADDR diubah sesuai untuk mengisi celah itu. Dalam aspek itu, Linux tidak seketat BSD.

Linux>=3.9

Linux 3.9 menambahkan opsi SO_REUSEPORT ke Linux juga. Opsi ini berperilaku persis seperti opsi di BSD dan memungkinkan pengikatan ke alamat dan nomor port yang persis sama selama semua soket memiliki opsi ini yang disetel sebelum mengikatnya.

Namun, masih ada dua perbedaan pada SO_REUSEPORT pada sistem lain:

  1. Untuk mencegah "pembajakan port", ada satu batasan khusus:Semua soket yang ingin berbagi alamat dan kombinasi port yang sama harus dimiliki oleh proses yang menggunakan ID pengguna efektif yang sama! Jadi satu pengguna tidak dapat "mencuri" port dari pengguna lain. Ini adalah keajaiban khusus untuk mengkompensasi SO_EXCLBIND yang hilang /SO_EXCLUSIVEADDRUSE bendera.

  2. Selain itu, kernel melakukan beberapa "keajaiban khusus" untuk SO_REUSEPORT soket yang tidak ditemukan di sistem operasi lain:Untuk soket UDP, ia mencoba mendistribusikan datagram secara merata, untuk soket pendengar TCP, ia mencoba mendistribusikan permintaan koneksi masuk (yang diterima dengan memanggil accept() ) secara merata di semua soket yang berbagi alamat dan kombinasi port yang sama. Dengan demikian aplikasi dapat dengan mudah membuka port yang sama dalam beberapa proses anak dan kemudian menggunakan SO_REUSEPORT untuk mendapatkan load balancing yang sangat murah.


Android

Meskipun keseluruhan sistem Android agak berbeda dari kebanyakan distribusi Linux, pada intinya bekerja kernel Linux yang sedikit dimodifikasi, jadi semua yang berlaku untuk Linux juga harus berlaku untuk Android.


Jendela

Windows hanya mengetahui SO_REUSEADDR opsi, tidak ada SO_REUSEPORT . Menyetel SO_REUSEADDR pada soket di Windows berperilaku seperti menyetel SO_REUSEPORT dan SO_REUSEADDR pada soket di BSD, dengan satu pengecualian:

Sebelum Windows 2003, soket dengan SO_REUSEADDR selalu dapat diikat ke alamat sumber dan port yang persis sama dengan soket yang sudah terikat, bahkan jika soket lain tidak memiliki opsi ini saat terikat . Perilaku ini memungkinkan aplikasi "mencuri" port yang terhubung dari aplikasi lain. Tak perlu dikatakan bahwa ini memiliki implikasi keamanan yang besar!

Microsoft menyadarinya dan menambahkan opsi soket penting lainnya:SO_EXCLUSIVEADDRUSE . Menyetel SO_EXCLUSIVEADDRUSE pada soket memastikan bahwa jika pengikatan berhasil, kombinasi alamat sumber dan port dimiliki secara eksklusif oleh soket ini dan tidak ada soket lain yang dapat mengikatnya, bahkan jika memiliki SO_REUSEADDR atur.

Perilaku default ini diubah pertama kali pada Windows 2003, Microsoft menyebutnya "Keamanan Soket yang Ditingkatkan" (nama lucu untuk perilaku yang merupakan default pada semua sistem operasi utama lainnya). Untuk lebih jelasnya kunjungi saja halaman ini. Ada tiga tabel:Yang pertama menunjukkan perilaku klasik (masih digunakan saat menggunakan mode kompatibilitas!), yang kedua menunjukkan perilaku Windows 2003 ke atas saat bind() panggilan dilakukan oleh pengguna yang sama, dan yang ketiga saat bind() panggilan dilakukan oleh pengguna yang berbeda.


Solaris

Solaris adalah penerus SunOS. SunOS awalnya didasarkan pada fork BSD, SunOS 5 dan kemudian didasarkan pada fork SVR4, namun SVR4 adalah gabungan dari BSD, System V, dan Xenix, jadi sampai tingkat tertentu Solaris juga merupakan fork BSD, dan sebuah agak awal. Akibatnya Solaris hanya mengetahui SO_REUSEADDR , tidak ada SO_REUSEPORT . SO_REUSEADDR berperilaku hampir sama seperti di BSD. Sejauh yang saya tahu tidak ada cara untuk mendapatkan perilaku yang sama seperti SO_REUSEPORT di Solaris, itu berarti tidak mungkin mengikat dua soket ke alamat dan port yang persis sama.

Mirip dengan Windows, Solaris memiliki opsi untuk memberikan pengikatan eksklusif pada soket. Opsi ini bernama SO_EXCLBIND . Jika opsi ini disetel pada soket sebelum mengikatnya, setel SO_REUSEADDR pada soket lain tidak berpengaruh jika kedua soket diuji untuk konflik alamat. Misalnya. jika socketA terikat ke alamat karakter pengganti dan socketB memiliki SO_REUSEADDR diaktifkan dan terikat ke alamat non-karakter pengganti dan port yang sama dengan socketA , pengikatan ini biasanya akan berhasil, kecuali socketA memiliki SO_EXCLBIND diaktifkan, dalam hal ini akan gagal terlepas dari SO_REUSEADDR bendera socketB .


Sistem Lain

Jika sistem Anda tidak tercantum di atas, saya menulis sebuah program uji kecil yang dapat Anda gunakan untuk mengetahui bagaimana sistem Anda menangani kedua opsi ini. Juga jika menurut Anda hasil saya salah , harap jalankan program itu terlebih dahulu sebelum mengeposkan komentar apa pun dan kemungkinan membuat klaim palsu.

Semua yang diperlukan kode untuk dibuat hanyalah sedikit API POSIX (untuk bagian jaringan) dan kompiler C99 (sebenarnya sebagian besar kompiler non-C99 akan berfungsi dengan baik selama mereka menawarkan inttypes.h dan stdbool.h; misalnya gcc didukung keduanya jauh sebelum menawarkan dukungan penuh C99).

Semua yang perlu dijalankan oleh program adalah bahwa setidaknya satu antarmuka di sistem Anda (selain antarmuka lokal) memiliki alamat IP yang ditetapkan dan rute default ditetapkan yang menggunakan antarmuka itu. Program akan mengumpulkan alamat IP tersebut dan menggunakannya sebagai "alamat spesifik" kedua.

Ini menguji semua kemungkinan kombinasi yang dapat Anda pikirkan:

  • Protokol TCP dan UDP
  • Soket normal, soket dengar (server), soket multicast
  • SO_REUSEADDR atur pada soket1, soket2, atau kedua soket
  • SO_REUSEPORT atur pada soket1, soket2, atau kedua soket
  • Semua kombinasi alamat yang dapat Anda buat dari 0.0.0.0 (karakter pengganti), 127.0.0.1 (alamat spesifik), dan alamat spesifik kedua yang ditemukan di antarmuka utama Anda (untuk multicast hanya 224.1.2.3 di semua pengujian)

dan mencetak hasilnya dalam tabel yang bagus. Ini juga akan berfungsi pada sistem yang tidak mengetahui SO_REUSEPORT , dalam hal ini opsi ini tidak diuji.

Apa yang tidak dapat dengan mudah diuji oleh program adalah bagaimana SO_REUSEADDR bekerja pada soket di TIME_WAIT nyatakan karena sangat sulit untuk memaksa dan mempertahankan soket dalam keadaan itu. Untungnya sebagian besar sistem operasi tampaknya berperilaku seperti BSD di sini dan sebagian besar pemrogram dapat mengabaikan keberadaan status tersebut.

Berikut kodenya (saya tidak dapat memasukkannya di sini, jawaban memiliki batas ukuran dan kode akan mendorong balasan ini melebihi batas).


Jawaban Mecki benar-benar sempurna, tetapi perlu ditambahkan bahwa FreeBSD juga mendukung SO_REUSEPORT_LB , yang meniru SO_REUSEPORT Linux perilaku - menyeimbangkan beban; lihat setockopt(2)


Linux
  1. Apa itu Podman dan Apa Bedanya dengan Docker?

  2. Cara:Replikasi dan Konfigurasi DRBD

  3. Bagaimana cara menangani revent Linux socket POLLERR, POLLHUP dan POLLNVAL?

  1. Cara Mengelola dan Mendaftar Layanan di Linux

  2. $bashpid Dan $$ Berbeda Dalam Beberapa Kasus?

  3. Cara:Pemrograman Socket dengan Python

  1. Cara dual-boot Linux dan Windows

  2. Bagaimana cara menghapus koneksi soket CLOSE_WAIT

  3. Apa perbedaan ulimit -n dan /proc/sys/fs/file-max?