Semua library HTTP yang akan kita pakai pada chapter berikutnya — urllib, requests, httpx — pada akhirnya berdiri di atas satu module standard library bernama socket. Dengan menulis komunikasi jaringan langsung di atas socket, kita melihat bentuk paling mentah dari koneksi TCP: bind, listen, accept, connect, send, dan recv. Pada Chapter ini kita akan membangun program echo server dan klien sungguhan yang saling bertukar pesan di localhost.

Socket, Titik Ujung Komunikasi #

Socket adalah titik ujung (endpoint) sebuah koneksi jaringan. Untuk membuat socket, kita menyebutkan dua informasi: address family (socket.AF_INET untuk IPv4) dan type (socket.SOCK_STREAM untuk TCP atau socket.SOCK_DGRAM untuk UDP). Server membuat socket lalu mengikatnya ke alamat dan port, sedangkan klien membuat socket lalu menyambungkannya ke alamat server.

String dan Bytes, Dua Dunia yang Wajib Diterjemahkan #

Aturan paling penting sebelum menulis kode socket: jaringan hanya mengirim dan menerima bytes, bukan str. Karena itu setiap pesan harus di-encode sebelum dikirim dan di-decode setelah diterima:

python
pesan = "Selamat pagi, Budi!"

data = pesan.encode("utf-8")
print(data)
print(type(data))

pesan_balik = data.decode("utf-8")
print(pesan_balik)

Output yang Dihasilkan:

text
b'Selamat pagi, Budi!'
<class 'bytes'>
Selamat pagi, Budi!

Mari kita telaah baris-baris kode di atas:

  1. Baris 3 (pesan.encode("utf-8")): Method .encode() mengubah string menjadi object bytes sesuai encoding UTF-8; huruf ASCII menjadi byte yang sama, huruf khusus menjadi dua-tiga byte.
  2. Baris 4-5: Tampilan b'...' menandakan tipe bytes, bukan str — type() mengonfirmasi hal tersebut.
  3. Baris 7 (data.decode("utf-8")): Method .decode() menerjemahkan kembali bytes menjadi string, dan keduanya harus memakai encoding yang sama.

Server dan Klien TCP Echo #

Program berikut adalah pasangan server-klien sungguhana. Server menerima pesan apa pun dari klien, lalu mengirimkan kembali pesan yang sama (itulah echo). Simpan sebagai server-echo.py:

python
import socket

server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
server.bind(("127.0.0.1", 7000))
server.listen()
print("Server echo berjalan di 127.0.0.1:7000")

while True:
    koneksi, alamat = server.accept()
    print(f"Klien terhubung dari {alamat[0]}")
    with koneksi:
        while True:
            data = koneksi.recv(1024)
            if not data:
                break
            koneksi.sendall(data)

Lalu simpan klien sebagai klien-echo.py:

python
import socket

with socket.create_connection(("127.0.0.1", 7000), timeout=3) as koneksi:
    koneksi.sendall("Halo, server!".encode("utf-8"))
    balasan = koneksi.recv(1024)
    print("Balasan:", balasan.decode("utf-8"))

Jalankan server di satu terminal (python3 server-echo.py), lalu klien di terminal kedua (python3 klien-echo.py).

Output di terminal server:

text
Server echo berjalan di 127.0.0.1:7000
Klien terhubung dari 127.0.0.1

Output di terminal klien:

text
Balasan: Halo, server!

Mari kita bedah kode kedua program tersebut:

  1. Baris 3-5 server (socket → bind → listen): Server membuat socket TCP, mengikatnya ke 127.0.0.1 port 7000, lalu mulai mendengarkan.
  2. Baris 9 (server.accept()): Function accept() memblokir eksekusi server sampai ada klien yang tersambung, lalu mengembalikan socket baru khusus untuk koneksi tersebut.
  3. Baris 13 (koneksi.recv(1024)): Method recv() menunggu data masuk; angka 1024 adalah ukuran maksimal potongan data yang dibaca sekali baca.
  4. Baris 14-15 (if not data: break): Saat klien menutup koneksi, recv() mengembalikan b'' yang bernilai salah, sehingga loop berhenti dan server siap menerima klien berikutnya.
  5. Baris 3 klien (socket.create_connection(...)): Function praktis yang membuat socket, memanggil connect() ke alamat tujuan, sekaligus memasang timeout=3 detik; blok with memastikan koneksi tertutup otomatis.
  6. Baris 4-5 klien: Pesan "Halo, server!" di-encode menjadi bytes oleh sendall(), lalu balasan dari recv() di-decode kembali menjadi string untuk dicetak.

Sedikit Rasa UDP #

UDP tidak memakai koneksi sama sekali — data dikirim sebagai datagram dengan alamat tujuan menempel pada setiap paket. Simpan sebagai server-udp.py:

python
import socket

server = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
server.bind(("127.0.0.1", 7001))
print("Server UDP berjalan di 127.0.0.1:7001")

while True:
    data, alamat = server.recvfrom(1024)
    print("Diterima:", data.decode("utf-8"))
    server.sendto(data.upper(), alamat)

Dan klien klien-udp.py:

python
import socket

klien = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
klien.settimeout(3)

klien.sendto("belajar jaringan".encode("utf-8"), ("127.0.0.1", 7001))
balasan, _ = klien.recvfrom(1024)
print("Balasan:", balasan.decode("utf-8"))

Output di terminal klien:

text
Balasan: BELAJAR JARINGAN

Perhatikan dua perbedaan utamanya: server tidak memanggil listen() dan accept(), serta method recvfrom() mengembalikan pasangan (data, alamat) karena setiap datagram datang tanpa koneksi — server harus tahu alamat pengirim agar tahu ke mana membalas melalui sendto(). Dengan bekal socket ini, kita siap naik ke protokol yang memakainya paling banyak: HTTP.