Melakukan Binding Port Yang Sama Di Go Dengan SO_REUSEPORT
Sebuah socket di sistem operasi berbasis UNIX adalah kombinasi dari alamat IP sumber, port sumber, alamat IP tujuan dan port tujuan. Pada umumnya, bila sebuah program ingin membuat socket baru, kombinasi dari ke-empat elemen tersebut harus unik. Untuk membuktikannya, saya akan membuat sebuah program yang melakukan binding di port 12345/UDP
dan pada saat yang bersamaan, juga mengirim pesan dari port 12345/UDP
ke alamat multicast 239.255.255.259
di port 3702/UDP
. Contoh kode program Go-nya terlihat seperti berikut ini:
Pada kode program di atas, terdapat goroutine listen()
yang menerima koneksi UDP masuk di port 12345
dan goroutine send()
yang
mengirim pesan UDP dari port 12345
ke port 3702
di alamat IP multicast. Terlihat bahwa kedua goroutine tersebut menggunakan
port 12345
yang sama. Apa yang terjadi saat program dijalankan? Saya akan menemukan pesan kesalahan seperti berikut ini:
Untuk mengatasi pesan kesalahan di atas, pada sistem operasi Ubuntu, saya perlu menggunakan flag SO_REUSEPORT
saat
membuat socket. SO_REUSEPORT
yang sudah ada sejak kernel Linux 3.9 memungkinkan sebuah aplikasi untuk menggunakan alamat IP
dan port yang sama berulang kali. Demi alasan keamanan, hal ini hanya bisa dilakukan bila user id (UID) program yang
menggunakan SO_REUSEPORT
sama dengan UID program yang sudah menggunakan socket tersebut lebih
awal. Selain SO_REUSEPORT
, juga ada pilihan SO_REUSEADDR
yang lebih lama. Berbeda dengan SO_REUSEPORT
, SO_REUSEADDR
tidak memiliki mekanisme untuk mencegah terjadinya port hijacking.
Walaupun bagian dari fitur resmi Go, flag SO_REUSEPORT
merupakan fitur eksperimental yang bersifat low level dan tidak
bisa jalan di sistem operasi sehingga ia diletakkan di package terpisah. Untuk itu, saya perlu menambahkannya dengan
memberikan perintah seperti berikut ini:
Setelah itu, saya kemudian mengubah kode program saya sehingga terlihat seperti berikut ini:
Pada kode program di atas, saya membuat instance dari structure ListenConfig
. Saya dapat mendefinisikan kustomisasi
pada socket yang dipakai melalui nilai Control
. Sebagai contoh, pada kode program di atas, saya memanggil
unix.SetsockoptInt(int(fd), unix.SOL_SOCKET, unix.SO_REUSEPORT, 1)
untuk mengaktifkan flag SO_REUSEPORT
. Setelah itu,
saya dapat memanggil method ListenPacket()
milik ListenConfig
untuk mempersiapkan socket dan mendapatkan PacketConn
.
Saya kemudian menggunakan method ReadFrom()
untuk menerima pesan atau method WriteTo()
untuk mengirim pesan.
Saat kode program kembali dijalankan, saya tidak akan menemukan pesan kesalahan lagi. Selain itu, saya dapat mencoba menjalankan program tersebut lebih dari sekali pada saat bersamaan (misalnya di Terminal baru tanpa menutup program sebelumnya). Walaupun dijalankan lebih dari sekali dan menggunakan port yang sama, program tetap akan bekerja seperti biasanya.