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:
-
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 memilikiSO_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 menyetelSO_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. -
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 adaSO_REUSEPORT
sebelum 3.9, perilakuSO_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:
-
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. -
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 memanggilaccept()
) 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 menggunakanSO_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 soketSO_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 hanya224.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)