Ingin membangun program server dan klien TCP yang dapat menangani puluhan permintaan secara bersamaan (concurrent)? Artikel ini akan memandu Anda, mulai dari memahami konsep socket hingga membuat program GoLang yang handal.
Apa itu Socket?
Secara sederhana, socket adalah mekanisme yang memungkinkan dua komputer terhubung satu sama lain. Bayangkan seperti saluran telepon kabel, di mana kedua orang dapat berbicara dan pesan ditransfer melalui kabel. Namun, untuk komputer dan jaringan, konsepnya lebih kompleks, tetapi idenya tetap sama.
Membangun Server di GoLang:
- Inisiasi Projek:
Pertama, buat project baru di direktori pilihan Anda. Gunakan go mod init
untuk inisiasi dan beri nama project sesuai keinginan (idealnya sama dengan nama repository GitHub Anda). Sebagai contoh, kita akan menggunakan github.com/milbmr/golang-server
.
Bash
go mod init github.com/milbmr/golang-server
Use code with caution.content_copy
- Buat File main.go:
Buat file main.go
menggunakan editor teks favorit Anda. Pastikan untuk memberi nama package menjadi package main
dan import package yang dibutuhkan: net
dan fmt
dari standard library. Terakhir, buat fungsi main
.
Go
package main
import (
"fmt"
"net"
)
func main() {
// ... isi kode server disini
}
Use code with caution.content_copy
- Membuat Listener:
Listener adalah socket yang menunggu koneksi pada alamat IP dan port tertentu. Tambahkan kode berikut ke fungsi main
dan kita akan membahasnya baris per baris:
Go
listener, err := net.Listen("tcp", "127.0.0.1:8000")
if err != nil {
fmt.Println("Error mendengarkan koneksi:", err)
return
}
Use code with caution.content_copy
- Argumen pertama untuk fungsi
listen
adalah protokol jaringan yang akan kita gunakan. Dalam kasus ini, kita menggunakan protokol TCP untuk membuat koneksi jaringan yang persisten dan handal. - Argumen kedua adalah alamat IP dan nomor port (8000) tempat socket akan terikat untuk mendengarkan koneksi masuk.
- Kita menggunakan alamat loopback “127.0.0.1” karena kita ingin socket ini diakses secara lokal pada mesin kita, bukan terekspos ke jaringan untuk lalu lintas jarak jauh.
Fungsi listen
akan mengembalikan interface net.Listener
dan error. Kita periksa error menggunakan pernyataan if
untuk memastikan semuanya berjalan dengan baik.
- Menangani Koneksi:
Tambahkan kode berikut di bawah pembuatan listener:
Go
go func() {
for {
conn, err := listener.Accept()
if err != nil {
return
}
go func(c net.Conn) {
defer c.Close()
_, err := c.Write([]byte("connected\n"))
if err != nil {
fmt.Println("Error menulis:", err)
return
}
}(conn)
}
}()
Use code with caution.content_copy
- Kita membuat fungsi anonim dan menjalankannya di goroutine terpisah untuk menangani permintaan secara asynchronous (tidak bergantung).
- Looping infinite (
for
) diperlukan kecuali kita hanya ingin menangani satu permintaan. - Di dalam loop, kita menggunakan method
Accept
dari interfacenet.Listener
yang dikembalikan olehnet.Listen
. Method ini akan menunggu hingga ada koneksi baru kemudian mengembalikan interfacenet.Conn
. Interface ini memiliki methodRead
danWrite
untuk memungkinkan kita membaca dan menulis ke aliran koneksi TCP. - Selanjutnya, kita membuat goroutine baru untuk menangani permintaan yang diterima oleh method
Accept
. Interfaceconn
dilewatkan sebagai argumen ke goroutine untuk memastikan setiap permintaan dienkapsulasi dalam goroutine terpisah. - Pernyataan
defer
digunakan untuk memastikan koneksi ditutup sebelum goroutine keluar. - Kita gunakan method
Write
untuk mengirim data kembali ke klien. Method ini membutuhkan byte slice sebagai argumen, jadi kita perlu mengonversi string menjadi byte slice sebelum ditulis. - Terakhir, kita tangani error dan kembali ke loop
Accept
untuk siap menerima koneksi baru.
- Menunggu Goroutine dan Menutup Koneksi:
Server kita hampir selesai. Kita perlu menunggu goroutine untuk menyelesaikan tugasnya dan memastikan server berjalan dengan efisien. Selain itu, kita perlu menutup listener agar koneksi dapat diakhiri dengan baik setelah program selesai.
- Gunakan
WaitGroup
dari packagesync
untuk menunggu goroutine dan gunakan channel untuk menunggu fungsiAccept
selesai. - Tambahkan import untuk package
sync
di awal kode:
- Tambahkan import untuk package
sync
di awal kode:
Go
import (
"net"
"sync"
)
Use code with caution.content_copy
- Buat
WaitGroup
dan channel di fungsimain
:
Go
var w sync.WaitGroup
done := make(chan struct{})
Use code with caution.content_copy
- Ubah fungsi
goroutine
untuk melacak goroutine yang berjalan dan menyelesaikannya:
Go
var numberOfConnections int
go func() {
defer func() {
done <- struct{}{} // tulis ke channel
}()
for {
conn, err := listener.Accept()
if err != nil {
return
}
w.Add(1) // increment
go func(c net.Conn, connNum int) {
defer func() {
c.Close()
w.Done() // decrement
}()
_, err := c.Write([]byte(fmt.Sprintf("writing to connection number %d\n", connNum)))
if err != nil {
fmt.Println("Error menulis:", err)
return
}
}(conn, numberOfConnections)
numberOfConnections++
}
}()
Use code with caution.content_copy
- Gunakan
w.Add(1)
untuk menambah satu dengan setiap goroutine baru danw.Done()
untuk mengurangi saat selesai. - Tulis ke channel
done
untuk menunjukkan bahwa fungsiAccept
telah selesai dan loopfor
telah keluar. listener.Close()
memungkinkan koneksi ditutup dengan baik.w.Wait()
digunakan untuk menunggu goroutine selesai.
6. Membangun Klien:
- Buat fungsi
dial
untuk membuat permintaan ke server:
Go
func dial(wait *sync.WaitGroup) {
conn, err := net.Dial("tcp", "127.0.0.1:8000")
if err != nil {
fmt.Println("Error koneksi:", err)
return
}
defer func() {
conn.Close()
wait.Done()
}()
scanner := bufio.NewScanner(conn)
scanner.Scan()
err = scanner.Err()
if err != nil {
fmt.Println("Error scanning:", err)
}
fmt.Println(scanner.Text())
}
Use code with caution.content_copy
- Gunakan
net.Dial
untuk terhubung ke server, mirip dengan fungsinet.Listen
di server. - Gunakan
bufio.NewScanner
untuk membaca baris dari socket. - Cetak data yang diterima dari server.
7. Menjalankan Server dan Klien:
- Lakukan 100 permintaan serentak ke server:
Go
for i := 0; i < 100; i++ {
w.Add(1)
go dial(&w)
}
Use code with caution.content_copy
- Jalankan program menggunakan
go run main.go
.
Hasil:
Anda akan melihat output di terminal menunjukkan 100 koneksi simultan ke server.
Tips:
- Gunakan tools seperti
netstat
atautcpdump
untuk memantau koneksi jaringan. - Uji server dengan beban yang lebih tinggi untuk mengukur performanya.
- Gunakan teknik konkurensi lanjutan seperti
channels
dancontext
untuk mengelola goroutine dan aliran data dengan lebih baik.
Kesimpulan:
Anda telah membangun server dan klien TCP GoLang yang dapat menangani puluhan koneksi secara simultan. Dengan memahami konsep socket, goroutine, dan WaitGroup
, Anda dapat membangun aplikasi jaringan yang skalabel dan handal.
Ingat:
- Selalu pertimbangkan keamanan saat membangun aplikasi jaringan.
- Pantau performa server dan optimalkan kode Anda untuk menangani beban yang tinggi.
- Gunakan teknik konkurensi dengan hati-hati untuk menghindari deadlock dan race condition.