Socket.IO adalah library (bagian server dan bagian client) untuk mengirim event bernama bolak-balik, lengkap dengan reconnect otomatis, antrean saat putus, tanda terima, dan room. Dia berjalan di atas protokolnya sendiri, Engine.IO, yang memakai WebSocket (atau HTTP long polling kalau WebSocket gak tersedia) sebagai jalannya. Bayangin beda telepon biasa dan jasa kurir. WebSocket itu telepon: tersambung, tapi semua urusan kamu atur sendiri. Socket.IO itu kurir: paketnya berlabel ("status order"), ada tanda terima, paket yang dititipkan saat jalan ditutup diantar begitu jalan dibuka, dan bisa dikirim ke satu alamat atau satu kompleks sekaligus.
🧪 Tebakan cepat: Tablet Mas Eko menjalankan
socket.emit('order:status', { id: 12, status: 'Disetrika' }). Teks apa yang lewat di koneksi WebSocket-nya: JSON{"id":12,"status":"Disetrika"}saja, atau ada yang lain? Jawabannya ada di bagian "Di kabel: paket Engine.IO dan Socket.IO".
Server dan client
Server realtime Bu Rini di port 3000, memakai socket.io 4.8.4 (versi terbaru per Oktober 2026):
import { Server } from 'socket.io'
const io = new Server(3000, { cors: { origin: 'http://localhost:8000' } })
io.on('connection', (socket) => {
console.log('tersambung', socket.id)
socket.on('order:status', (data, ack) => {
// simpan ke database di sini
socket.broadcast.emit('order:status', data) // ke semua layar lain
ack({ ok: true }) // tanda terima ke pengirim
})
})
Di browser, dengan socket.io-client 4.8.4:
import { io } from 'socket.io-client'
const socket = io('http://localhost:3000')
socket.on('connect', () => console.log('tersambung', socket.id))
socket.on('order:status', ({ id, status }) => {
document.querySelector(`#status-${id}`).textContent = status
})
socket.emit('order:status', { id: 12, status: 'Disetrika' }, (balasan) => {
console.log(balasan) // { ok: true }
})
socket.emit(nama, data)mengirim event. Namanya bebas, datanya diubah jadi JSON otomatis (objek, array, angka, juga data biner).socket.on(nama, fn)mendengarkan event dengan nama itu, di kedua sisi.socket.broadcast.emit(...)di server mengirim ke semua client kecuali pengirimnya.io.emit(...)mengirim ke semua.corswajib di sini: papan dilocalhost:8000, server dilocalhost:3000, dan koneksi pertamanya request HTTP biasa.connect,disconnect, danconnect_erroradalah nama yang dipakai Socket.IO sendiri.socket.emit('connect')melempar error"connect" is a reserved event name.
Di kabel: paket Engine.IO dan Socket.IO
Ini isi koneksi yang direkam dari socket.io 4.8.4 (protokol Engine.IO versi 4, protokol Socket.IO versi 5):
GET /socket.io/?EIO=4&transport=polling
<- 0{"sid":"...","upgrades":["websocket"],"pingInterval":25000,"pingTimeout":20000,"maxPayload":1000000}
-> 40 <- 40{"sid":"..."}
-> 2probe <- 3probe -> 5 (pindah ke WebSocket)
-> 42["order:status",{"id":12,"status":"Disetrika"}]
-> 420["order:status",{"id":13,"status":"Siap diambil"}]
<- 430[{"ok":true}]
<- 2 -> 3 (ping dari server, pong dari client)
| Digit | Lapisan | Artinya |
|---|---|---|
| pertama | Engine.IO | 0 open, 1 close, 2 ping, 3 pong, 4 message, 5 upgrade, 6 noop |
| kedua (kalau yang pertama 4) | Socket.IO | 0 CONNECT, 1 DISCONNECT, 2 EVENT, 3 ACK, 4 CONNECT_ERROR, 5 BINARY_EVENT, 6 BINARY_ACK |
| sesudahnya | Socket.IO | nomor tanda terima (kalau ada), lalu array JSON [nama event, data...] |
Jadi 42["order:status",{...}] artinya: message (4), event (2), isinya nama dan data. Itu jawaban tebakannya: yang lewat bukan JSON polos, tapi paket ber-awalan. Akibatnya Socket.IO bukan WebSocket biasa. new WebSocket('ws://localhost:3000') gak bisa bicara dengan server Socket.IO (dicoba: koneksi ditolak), dan io() gak bisa tersambung ke server WebSocket biasa.
Client mulai lewat HTTP long polling (transport=polling), lalu pindah ke WebSocket lewat 2probe, 3probe, 5. Pilih io(url, { transports: ['websocket'] }) untuk langsung ke WebSocket.
Tanda terima: acknowledgement
Fungsi di argumen terakhir emit adalah tanda terima. Socket.IO memberi paketnya nomor, mulai dari 0 di tiap koneksi (420 = event dengan tanda terima nomor 0), dan server menjawab dengan nomor yang sama (430[{"ok":true}]).
socket.timeout(5000).emit('order:status', { id: 13, status: 'Siap diambil' }, (err, balasan) => {
if (err) console.log('server gak menjawab dalam 5 detik')
else console.log(balasan) // { ok: true }
})
const balasan = await socket.emitWithAck('order:status', { id: 14, status: 'Siap diambil' }) // versi Promise, sejak 4.6
Putus koneksi: reconnect dan antrean
- Reconnect otomatis, tanpa batas percobaan. Jedanya dari kode
socket.io-client4.8.4: mulai 1.000 ms, berlipat dua, paling lama 5.000 ms, diacak ±50%. Jadi percobaan pertama antara 500 dan 1.500 ms. - Antrean.
emitselama putus disimpan dan dikirim berurutan setelah tersambung lagi. Dicoba: dua status yang dikirim Mas Eko saat putus sampai ke server setelah reconnect, urutannya tetap. - Heartbeat. Server mengirim ping tiap 25 detik. Kalau pong gak datang dalam 20 detik, koneksinya dianggap mati.
- Alasan putus ada di
socket.on('disconnect', (reason) => ...):transport close(jaringan) disambung lagi otomatis, tapiio server disconnect(server memanggilsocket.disconnect()) gak. Panggilsocket.connect()sendiri.
Room, sekilas
Server bisa mengelompokkan socket ke dalam room. HP Pak Darto ikut room order:12, jadi kabar "Siap diambil" cuma sampai ke dia:
socket.join('order:12') // di server
io.to('order:12').emit('order:status', { id: 12, status: 'Siap diambil' })
Room cuma ada di server: client gak bisa masuk room sendiri. Detailnya di pelajaran Rooms & Namespaces.
Socket.IO atau WebSocket biasa?
- Pilih Socket.IO kalau butuh reconnect, antrean, tanda terima, dan room dengan cepat, dan kamu memegang server dan client-nya. Server Socket.IO juga ada untuk Python, Java, Go, dan lainnya, bukan cuma Node.js.
- Pilih WebSocket biasa (library
ws, atau WebSocket bawaan platform) kalau client-nya bukan Socket.IO (perangkat IoT, aplikasi pihak lain), kalau tiap byte dihitung, atau kalau platformnya memberi WebSocket mentah. Client Socket.IO sendiri 47.295 byte, 14.851 byte setelah gzip (socket.io.min.js4.8.4).
⚠️ Jebakan yang sering ketemu
- WebSocket biasa ke server Socket.IO. Gak tersambung, karena dua-duanya bicara protokol berbeda.
- Lupa
corsdi server. Request pertama ditolak browser, danconnect_errormuncul terus. - Menunggu reconnect setelah
socket.disconnect()dari server. Alasannyaio server disconnect, dan client gak menyambung sendiri. - Antrean basi. Setelah putus lama, semua
emityang tertahan dikirim sekaligus. Status yang sudah usang ikut terkirim, jadi server harus tahan menerima kabar lama.
TL;DR: Socket.IO adalah library di atas WebSocket (lewat protokol Engine.IO) yang menambah event bernama (emit dan on), tanda terima, reconnect otomatis (1 detik berlipat dua sampai 5 detik, diacak ±50%), antrean emit saat putus, fallback ke HTTP long polling, room, dan namespace. Di kabel, tiap paket punya awalan: 0 open, 2 ping, 3 pong, 40 connect, 42[...] event, 420[...] event dengan tanda terima nomor 0, dijawab 430[...]. Karena itu WebSocket biasa dan Socket.IO gak bisa saling sambung. Pakai socket.timeout(ms).emit(...) atau emitWithAck untuk tanda terima dengan batas waktu, dan pasang cors di server untuk origin papan.