Selamat datang di dunia portabilitas yang indah ... atau lebih tepatnya kekurangannya. Sebelum kita mulai menganalisis dua opsi ini secara terperinci dan melihat lebih dalam bagaimana berbagai sistem operasi menanganinya, perlu dicatat bahwa implementasi soket BSD adalah induk dari semua implementasi soket. Pada dasarnya semua sistem lain menyalin implementasi soket BSD di beberapa titik waktu (atau setidaknya interface-nya) dan kemudian mulai mengembangkannya sendiri. Tentu saja implementasi soket BSD juga dikembangkan pada saat yang sama dan dengan demikian sistem yang menyalinnya kemudian mendapatkan fitur yang kurang dalam sistem yang menyalinnya sebelumnya. Memahami implementasi soket BSD adalah kunci untuk memahami semua implementasi soket lainnya, jadi Anda harus membacanya bahkan jika Anda tidak peduli untuk pernah menulis kode untuk sistem BSD.
Ada beberapa dasar yang harus Anda ketahui sebelum kita melihat dua opsi ini. Koneksi TCP / UDP diidentifikasi oleh tuple dari lima nilai:
{<protocol>, <src addr>, <src port>, <dest addr>, <dest port>}
Kombinasi unik dari nilai-nilai ini mengidentifikasi koneksi. Akibatnya, tidak ada dua koneksi yang dapat memiliki nilai lima yang sama, jika tidak, sistem tidak akan dapat lagi membedakan koneksi ini.
Protokol soket diatur ketika 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 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 secara otomatis terikat sebelum akan terhubung.
Jika Anda secara eksplisit mengikat soket, Anda dapat 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 spesifik itu sendiri dalam hal itu (biasanya dari kisaran port sumber yang telah ditentukan sebelumnya, OS spesifik). Wildcard serupa ada untuk alamat sumber, yang bisa "alamat apa saja" ( 0.0.0.0
dalam kasus IPv4 dan::
dalam hal IPv6). Tidak seperti port, soket benar-benar dapat diikat ke "alamat apa pun" yang berarti "semua alamat IP sumber dari semua antarmuka lokal". Jika soket tersambung kemudian, sistem harus memilih alamat IP sumber tertentu, karena soket tidak dapat dihubungkan dan pada saat yang sama terikat ke alamat IP lokal apa pun. Bergantung pada alamat tujuan dan konten dari tabel routing, sistem akan memilih alamat sumber yang sesuai dan mengganti ikatan "apa saja" dengan yang mengikat ke alamat IP sumber yang dipilih.
Secara default, tidak ada dua soket yang dapat terikat pada kombinasi alamat sumber dan port sumber yang sama. Selama port sumber berbeda, alamat sumber sebenarnya tidak relevan. Mengikat socketA
untuk A:X
dan socketB
untuk B:Y
, di mana A
dan B
adalah alamat dan X
dan Y
adalah port, selalu mungkin selama X != Y
berlaku. Namun, bahkan jika X == Y
, pengikatan masih dimungkinkan selama A != B
benar. Misalnya socketA
milik program server FTP dan terikat 192.168.0.1:21
dan socketB
milik program server FTP lain dan terikat 10.0.0.1:21
, kedua binding akan berhasil. Perlu diingat, bahwa soket dapat diikat secara lokal ke "alamat apa pun". Jika soket terikat0.0.0.0:21
, ia terikat ke semua alamat lokal yang ada pada saat yang sama dan dalam hal ini tidak ada soket lain yang dapat terikat ke port 21
, terlepas dari alamat IP spesifik mana ia mencoba untuk mengikat, karena 0.0.0.0
konflik dengan semua alamat IP lokal yang ada.
Apa pun yang dikatakan sejauh ini hampir sama untuk semua sistem operasi utama. Hal-hal mulai mendapatkan spesifik OS ketika penggunaan kembali alamat mulai berlaku. Kita mulai dengan BSD, karena seperti yang saya katakan di atas, itu adalah ibu 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 dengan kombinasi yang sama persis antara alamat dan port sumber. Sekarang Anda mungkin bertanya-tanya bagaimana bedanya dengan yang sebelumnya? Kata kunci adalah "tepat". SO_REUSEADDR
terutama mengubah cara perlakuan alamat wildcard ("alamat IP") saat mencari konflik.
Tanpa SO_REUSEADDR
, mengikat socketA
untuk 0.0.0.0:21
kemudian mengikat socketB
ke 192.168.0.1:21
akan gagal (dengan kesalahan EADDRINUSE
), karena 0.0.0.0 berarti "alamat IP lokal", sehingga semua alamat IP lokal dianggap digunakan oleh socket ini dan ini termasuk 192.168.0.1
juga. Dengan SO_REUSEADDR
itu akan berhasil, karena 0.0.0.0
dan 192.168.0.1
yang tidak persis alamat yang sama, satu adalah wildcard untuk semua alamat lokal dan yang lain adalah alamat lokal yang sangat spesifik. Perhatikan bahwa pernyataan di atas benar terlepas dari urutannya socketA
dan socketB
terikat; tanpanya SO_REUSEADDR
akan selalu gagal, dengan SO_REUSEADDR
itu akan selalu berhasil.
Untuk memberi Anda gambaran umum yang lebih baik, mari buat tabel di sini dan buat daftar semua kemungkinan kombinasi:
SO_REUSEADDR socketA socketB Hasil
-------------------------------------------------- -------------------
ON / OFF 192.168.0.1:21 192.168.0.1:21 Kesalahan (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 Kesalahan (EADDRINUSE)
OFF 192.168.1.0:21 0.0.0.0:21 Kesalahan (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 Kesalahan (EADDRINUSE)
Tabel di atas mengasumsikan bahwa socketA
telah berhasil terikat ke alamat yang diberikan untuk socketA
, kemudian socketB
dibuat, apakah akan SO_REUSEADDR
ditetapkan atau tidak, dan akhirnya terikat ke alamat yang diberikan untuk socketB
. Result
adalah hasil dari operasi ikat untuk socketB
. Jika kolom pertama mengatakan ON/OFF
, nilai SO_REUSEADDR
tidak relevan dengan hasilnya.
Oke, SO_REUSEADDR
berpengaruh pada alamat wildcard, bagus untuk diketahui. Namun bukan itu efeknya saja. Ada efek terkenal lainnya yang juga merupakan alasan mengapa kebanyakan orang menggunakan SO_REUSEADDR
program server di tempat pertama. Untuk penggunaan penting lainnya dari opsi ini, kita harus melihat lebih dalam tentang cara kerja protokol TCP.
Soket memiliki buffer pengiriman dan jika panggilan ke send()
fungsi berhasil, itu tidak berarti bahwa data yang diminta benar-benar telah dikirim, itu hanya berarti data telah ditambahkan ke buffer pengirim. Untuk soket UDP, data biasanya dikirim segera, jika tidak segera, tetapi untuk soket TCP, mungkin ada penundaan yang relatif lama antara menambahkan data ke buffer pengiriman dan membuat implementasi TCP benar-benar mengirim data itu. Akibatnya, ketika Anda menutup soket TCP, mungkin masih ada data yang tertunda di buffer pengiriman, yang belum dikirim tetapi kode Anda menganggapnya sudah terkirim, karenasend()
panggilan berhasil. Jika implementasi TCP langsung menutup soket atas permintaan Anda, semua data ini akan hilang dan kode Anda bahkan tidak akan tahu tentang itu. TCP dikatakan sebagai protokol yang andal dan kehilangan data begitu saja tidak terlalu bisa diandalkan. Itu sebabnya soket yang masih memiliki data untuk dikirim akan masuk ke keadaan yang disebut TIME_WAIT
ketika Anda menutupnya. Dalam keadaan itu akan menunggu sampai semua data yang tertunda telah berhasil dikirim atau sampai batas waktu habis, dalam hal ini soket ditutup dengan paksa.
Jumlah waktu kernel akan menunggu sebelum menutup soket, terlepas dari apakah masih ada data dalam penerbangan atau tidak, disebut Linger Time . The Linger Waktu adalah global dikonfigurasi pada kebanyakan sistem dan secara default agak panjang (dua menit adalah nilai umum Anda akan menemukan pada banyak sistem). Ini juga dapat dikonfigurasi per soket menggunakan opsi soket SO_LINGER
yang dapat digunakan untuk membuat waktu habis lebih pendek atau lebih lama, dan bahkan untuk menonaktifkannya sepenuhnya. Menonaktifkan sepenuhnya adalah ide yang sangat buruk, karena menutup soket TCP dengan anggun adalah proses yang sedikit rumit dan melibatkan pengiriman dan pengembalian beberapa paket (serta mengirim ulang paket-paket tersebut jika hilang) dan seluruh proses penutupan ini juga dibatasi oleh Waktu Berlama-lama. Jika Anda menonaktifkan sisa, soket Anda mungkin tidak hanya kehilangan data dalam penerbangan, itu juga selalu ditutup dengan paksa alih-alih dengan anggun, yang biasanya tidak disarankan. Rincian tentang bagaimana koneksi TCP ditutup dengan anggun berada di luar cakupan jawaban ini, jika Anda ingin mempelajari lebih lanjut, saya sarankan Anda melihat halaman ini . Dan bahkan jika Anda menonaktifkan berlama-lama dengan SO_LINGER
, jika proses Anda mati tanpa secara eksplisit menutup soket, BSD (dan mungkin sistem lain) akan tetap ada, mengabaikan apa yang telah Anda konfigurasikan. Ini akan terjadi misalnya jika kode Anda hanya panggilanexit()
(cukup umum untuk program server yang kecil dan sederhana) atau proses tersebut dimatikan oleh sinyal (yang mencakup kemungkinan bahwa itu hanya crash karena akses memori ilegal). Jadi tidak ada yang dapat Anda lakukan untuk memastikan soket tidak akan pernah tertinggal dalam semua keadaan.
Pertanyaannya adalah, bagaimana sistem memperlakukan soket dalam keadaan TIME_WAIT
? Jika SO_REUSEADDR
tidak disetel, soket dalam keadaan 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 ditutup, yang mungkin memakan waktu lama sebagai Linger Time yang dikonfigurasi . Jadi jangan berharap bahwa Anda dapat mengubah alamat sumber soket segera setelah menutupnya. Dalam kebanyakan kasus ini akan gagal. Namun, jika SO_REUSEADDR
diatur untuk soket yang Anda coba ikat, soket lain terikat ke alamat dan port yang sama dalam keadaanTIME_WAIT
diabaikan, setelah semua yang sudah "setengah mati", dan soket Anda dapat mengikat ke alamat yang sama tanpa masalah. Dalam hal ini tidak ada peran yang soket lainnya mungkin memiliki alamat dan port yang sama persis. Perhatikan bahwa mengikat soket ke alamat dan port yang sama persis dengan soket sekarat di TIME_WAIT
negara bagian dapat memiliki efek samping yang tidak diharapkan, dan biasanya tidak diinginkan, seandainya soket lainnya masih "bekerja", tetapi itu berada di luar cakupan jawaban ini dan untungnya efek samping tersebut agak jarang dalam praktek.
Ada satu hal terakhir yang harus Anda ketahui SO_REUSEADDR
. Segala sesuatu yang tertulis di atas akan berfungsi selama soket yang ingin Anda ikat telah mengaktifkan kembali alamat. Tidak perlu bahwa soket lain, yang sudah terikat atau dalam TIME_WAIT
keadaan, juga memiliki bendera ini ditetapkan ketika terikat. Kode yang memutuskan apakah ikatan akan berhasil atau gagal hanya memeriksa SO_REUSEADDR
bendera soket dimasukkan ke dalam bind()
panggilan, untuk semua soket lainnya diperiksa, bendera ini bahkan tidak melihat.
SO_REUSEPORT
SO_REUSEPORT
adalah apa yang diharapkan kebanyakan orang SO_REUSEADDR
. Pada dasarnya, SO_REUSEPORT
memungkinkan Anda untuk mengikat jumlah sewenang-wenang soket untuk persis alamat sumber yang sama dan port asalkan semua soket terikat sebelum juga telah SO_REUSEPORT
ditetapkan sebelum mereka terikat. Jika soket pertama yang terikat pada alamat dan port tidak SO_REUSEPORT
diatur, tidak ada soket lain yang dapat diikat ke alamat dan port yang sama persis, terlepas dari apakah soket lain ini telah SO_REUSEPORT
disetel atau tidak, sampai soket pertama melepaskan ikatannya lagi. Tidak seperti dalam kasus SO_REUESADDR
penanganan kode SO_REUSEPORT
tidak hanya akan memverifikasi bahwa soket terikat saat ini telah SO_REUSEPORT
ditetapkan tetapi juga akan memverifikasi bahwa soket dengan alamat dan port yang saling bertentangan telah SO_REUSEPORT
ditetapkan ketika terikat.
SO_REUSEPORT
tidak menyiratkan SO_REUSEADDR
. Ini berarti jika soket tidak SO_REUSEPORT
diatur ketika terikat dan soket lain telah SO_REUSEPORT
ditetapkan ketika terikat ke alamat dan port yang sama persis, ikatan gagal, yang diharapkan, tetapi juga gagal jika soket lainnya sudah sekarat dan dalam TIME_WAIT
keadaan. Untuk dapat mengikat soket ke alamat dan port yang sama seperti soket lain dalam TIME_WAIT
keadaan mengharuskan SO_REUSEADDR
untuk ditetapkan pada soket itu atau SO_REUSEPORT
harus telah ditetapkan pada kedua soket sebelum mengikatnya. Tentu saja diperbolehkan untuk mengatur keduanya, SO_REUSEPORT
dan SO_REUSEADDR
, pada soket.
Tidak ada banyak yang bisa dikatakan tentang SO_REUSEPORT
selain itu ditambahkan kemudian SO_REUSEADDR
, itu sebabnya Anda tidak akan menemukannya di banyak implementasi soket dari sistem lain, yang "bercabang" kode BSD sebelum opsi ini ditambahkan, dan bahwa tidak ada cara untuk mengikat dua soket ke alamat soket yang sama persis di BSD sebelum opsi ini.
Connect () Mengembalikan EADDRINUSE?
Sebagian besar orang tahu bahwa bind()
mungkin gagal dengan kesalahan EADDRINUSE
, namun, ketika Anda mulai bermain-main dengan penggunaan kembali alamat, Anda mungkin mengalami situasi aneh yang connect()
gagal dengan kesalahan itu juga. Bagaimana ini bisa terjadi? Bagaimana alamat jarak jauh, setelah semua itu yang terhubung menambah soket, sudah bisa digunakan? Menghubungkan banyak 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, koneksi didefinisikan oleh tuple dari lima nilai, ingat? Dan saya juga berkata, bahwa kelima nilai ini harus unik jika tidak, sistem tidak dapat lagi membedakan dua koneksi, kan? Nah, dengan penggunaan kembali alamat, Anda dapat mengikat dua soket protokol yang sama ke alamat sumber dan port yang sama. Itu berarti tiga dari lima nilai tersebut sudah sama untuk dua 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 bisa, paling tidak untuk koneksi TCP (koneksi UDP sebenarnya bukan koneksi yang sebenarnya). Jika data tiba untuk salah satu dari dua koneksi, sistem tidak dapat menentukan koneksi milik data tersebut.
Jadi jika Anda mengikat dua soket protokol yang sama ke alamat sumber dan port yang sama dan mencoba menghubungkan keduanya ke alamat tujuan dan port yang sama, connect()
sebenarnya akan gagal dengan kesalahan EADDRINUSE
untuk soket kedua yang Anda coba sambungkan, yang berarti bahwa soket dengan tuple identik dari lima nilai sudah terhubung.
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 mengetahui tentang IPv6 tetapi alamat multicast juga ada di IPv4, meskipun fitur ini tidak pernah digunakan secara luas di Internet publik.
Arti SO_REUSEADDR
perubahan untuk alamat multicast karena memungkinkan beberapa soket terikat ke kombinasi yang sama persis dari alamat dan port multicast sumber. Dengan kata lain, untuk alamat multicast SO_REUSEADDR
berperilaku sama seperti SO_REUSEPORT
untuk alamat unicast. Sebenarnya, kode memperlakukan SO_REUSEADDR
dan SO_REUSEPORT
identik untuk alamat multicast, itu berarti Anda bisa mengatakan itu 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 mereka bertiga menawarkan opsi yang sama dengan BSD dan mereka juga berperilaku seperti di BSD.
macOS (MacOS X)
Pada intinya, macOS hanyalah sebuah UNIX gaya BSD bernama " Darwin ", berdasarkan garpu yang agak terlambat dari kode BSD (BSD 4.3), yang kemudian disinkronkan lagi dengan FreeBSD (pada waktu itu) 5 basis kode untuk rilis Mac OS 10.3, sehingga Apple bisa mendapatkan kepatuhan POSIX penuh (macOS adalah POSIX bersertifikat). Meskipun memiliki microkernel pada intinya (" Mach "), sisa dari kernel (" XNU ") pada dasarnya hanya sebuah kernel BSD, dan itulah sebabnya macOS menawarkan opsi yang sama seperti BSD dan mereka juga berperilaku sama seperti pada BSD .
iOS / watchOS / tvOS
iOS hanyalah garpu macOS dengan kernel yang sedikit dimodifikasi dan dipangkas, agak dipangkas toolset ruang pengguna dan set kerangka kerja standar yang sedikit berbeda. watchOS dan tvOS adalah garpu iOS, yang dipreteli lebih jauh (terutama watchOS). Sepengetahuan terbaik saya, mereka semua berperilaku persis seperti halnya macOS.
Linux
Linux <3.9
Sebelum ke Linux 3.9, hanya opsi yang SO_REUSEADDR
ada. Opsi ini berlaku secara umum sama seperti di BSD dengan dua pengecualian penting:
Selama soket TCP mendengarkan (server) terikat ke port tertentu, SO_REUSEADDR
opsi ini sepenuhnya diabaikan untuk semua soket yang menargetkan port itu. Mengikat soket kedua ke port yang sama hanya mungkin jika itu juga mungkin di BSD tanpa harus SO_REUSEADDR
mengatur. Misalnya Anda tidak dapat mengikat ke alamat wildcard dan kemudian ke yang lebih spesifik atau sebaliknya, keduanya dimungkinkan di BSD jika Anda atur SO_REUSEADDR
. Apa yang dapat Anda lakukan adalah Anda dapat mengikat ke port yang sama dan dua alamat non-wildcard yang berbeda, seperti yang selalu diperbolehkan. Dalam aspek ini Linux lebih membatasi daripada BSD.
Pengecualian kedua adalah bahwa untuk soket klien, opsi ini berperilaku persis seperti SO_REUSEPORT
di BSD, selama keduanya telah menetapkan flag ini sebelum terikat. Alasan untuk mengizinkannya adalah karena penting untuk dapat mengikat beberapa soket dengan tepat ke alamat soket UDP yang sama untuk berbagai protokol dan karena SO_REUSEPORT
sebelumnya tidak ada sebelum 3.9, perilaku SO_REUSEADDR
diubah sesuai untuk mengisi celah tersebut . 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 sama persis selama semua soket memiliki opsi ini ditetapkan sebelum mengikatnya.
Namun, masih ada dua perbedaan 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 memiliki ID pengguna efektif yang sama! Jadi satu pengguna tidak bisa "mencuri" port dari pengguna lain. Ini adalah sihir khusus untuk mengimbangi hilang SO_EXCLBIND
/ SO_EXCLUSIVEADDRUSE
bendera.
Selain itu kernel melakukan beberapa "sihir khusus" untuk SO_REUSEPORT
soket yang tidak ditemukan di sistem operasi lain: Untuk soket UDP, ia mencoba untuk mendistribusikan datagram secara merata, untuk soket pendengar TCP, ia mencoba untuk mendistribusikan permintaan koneksi masuk (yang diterima dengan menelepon accept()
) 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 gunakan 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, sehingga segala sesuatu yang berlaku untuk Linux harus berlaku untuk Android juga.
Windows
Windows hanya tahu SO_REUSEADDR
pilihannya, tidak ada SO_REUSEPORT
. Pengaturan SO_REUSEADDR
pada soket di Windows berperilaku seperti pengaturan SO_REUSEPORT
dan SO_REUSEADDR
pada soket di BSD, dengan satu pengecualian: Soket dengan SO_REUSEADDR
selalu dapat mengikat ke alamat sumber dan port yang sama persis dengan soket yang sudah terikat, bahkan jika soket lainnya tidak memiliki opsi ini. atur kapan itu diikat . Perilaku ini agak berbahaya karena memungkinkan aplikasi "mencuri" port yang terhubung dari aplikasi lain. Tak perlu dikatakan, ini dapat memiliki implikasi keamanan besar. Microsoft menyadari bahwa ini mungkin menjadi masalah dan dengan demikian menambahkan opsi soket lainnya SO_EXCLUSIVEADDRUSE
. PengaturanSO_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 telah SO_REUSEADDR
diatur.
Untuk detail lebih lanjut tentang bagaimana flag SO_REUSEADDR
dan SO_EXCLUSIVEADDRUSE
bekerja di Windows, bagaimana mereka mempengaruhi binding / re-binding, Microsoft dengan ramah menyediakan tabel yang mirip dengan tabel saya di dekat bagian atas balasan itu. Cukup kunjungi halaman ini dan gulirkan sedikit ke bawah. Sebenarnya ada tiga tabel, yang pertama menunjukkan perilaku lama (sebelum Windows 2003), yang kedua perilaku (Windows 2003 dan lebih tinggi) dan yang ketiga menunjukkan bagaimana perilaku berubah di Windows 2003 dan kemudian jika bind()
panggilan dilakukan oleh pengguna yang berbeda.
Solaris
Solaris adalah penerus SunOS. SunOS awalnya didasarkan pada garpu BSD, SunOS 5 dan kemudian didasarkan pada garpu SVR4, namun SVR4 merupakan gabungan dari BSD, System V, dan Xenix, sehingga sampai tingkat tertentu Solaris juga merupakan garpu BSD, dan yang agak awal. Akibatnya Solaris hanya tahu SO_REUSEADDR
, tidak ada SO_REUSEPORT
. The SO_REUSEADDR
berperilaku hampir sama seperti halnya di BSD. Sejauh yang saya tahu tidak ada cara untuk mendapatkan perilaku yang sama seperti SO_REUSEPORT
di Solaris, itu berarti tidak mungkin untuk mengikat dua soket ke alamat dan port yang sama persis.
Mirip dengan Windows, Solaris memiliki opsi untuk memberi soket pengikatan eksklusif. Opsi ini dinamai SO_EXCLBIND
. Jika opsi ini diatur pada soket sebelum mengikatnya, pengaturan SO_REUSEADDR
pada soket lain tidak akan berpengaruh jika kedua soket diuji untuk konflik alamat. Misalnya jika socketA
terikat ke alamat wildcard dan socketB
telah SO_REUSEADDR
diaktifkan dan terikat ke alamat non-wildcard dan port yang sama dengan socketA
, ikatan ini biasanya akan berhasil, kecuali jika socketA
telah SO_EXCLBIND
diaktifkan, dalam hal ini akan gagal terlepas dari SO_REUSEADDR
bendera socketB
.
Sistem Lainnya
Jika sistem Anda tidak tercantum di atas, saya menulis sebuah program uji kecil yang dapat Anda gunakan untuk mengetahui bagaimana sistem Anda menangani dua opsi ini. Jika menurut Anda hasil saya salah , jalankan dulu program itu sebelum mengeposkan komentar dan mungkin membuat klaim palsu.
Semua yang diperlukan oleh kode untuk dibuat adalah API POSIX sedikit (untuk bagian-bagian jaringan) dan kompiler C99 (sebenarnya sebagian besar kompiler non-C99 akan bekerja sebaik yang mereka tawarkan inttypes.h
dan stdbool.h
; mis. gcc
Didukung keduanya jauh sebelum menawarkan dukungan penuh C99) .
Yang perlu dijalankan oleh program adalah 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 itu dan menggunakannya sebagai "alamat spesifik" kedua.
Ini menguji semua kemungkinan kombinasi yang dapat Anda pikirkan:
- Protokol TCP dan UDP
- Soket normal, soket pendengar (server), soket multicast
SO_REUSEADDR
atur pada socket1, socket2, atau kedua soket
SO_REUSEPORT
atur pada socket1, socket2, atau kedua soket
- Semua kombinasi alamat yang dapat Anda buat dari
0.0.0.0
(wildcard), 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 tes)
dan mencetak hasilnya dalam tabel yang bagus. Ini juga akan bekerja pada sistem yang tidak tahu SO_REUSEPORT
, dalam hal ini opsi ini tidak diuji.
Apa yang tidak bisa dengan mudah diuji oleh program adalah bagaimana SO_REUSEADDR
bertindak pada soket dalam TIME_WAIT
keadaan seperti itu sangat sulit untuk memaksa dan menyimpan soket dalam keadaan itu. Untungnya sebagian besar sistem operasi tampaknya hanya berperilaku seperti BSD di sini dan sebagian besar programmer waktu dapat dengan mudah mengabaikan keberadaan negara itu.
Inilah kodenya (saya tidak bisa memasukkannya di sini, jawaban memiliki batas ukuran dan kode akan mendorong balasan ini melebihi batas).
INADDR_ANY
tidak mengikat alamat lokal yang ada, tetapi semua yang akan datang juga.listen
tentu saja membuat soket dengan protokol, alamat lokal, dan port lokal yang sama persis, meskipun Anda mengatakan itu tidak mungkin.