Setiap kali kamu memanggil API lewat requests, query ke PostgreSQL, atau clone repo lewat SSH, di baliknya ada satu hal yang sama: socket. Semua komunikasi jaringan di Python — HTTP, database, bahkan server game — berjalan di atas socket. Daripada terus memakai library yang menyembunyikan lapisan ini, kamu bisa menulis server TCP sendiri pakai modul bawaan tanpa dependency apa pun. Hasil akhirnya: chat server yang bisa dipakai beberapa client sekaligus.
Kenapa Belajar Socket, Bukan Cuma Pakai requests?
Kebanyakan developer cuma menyentuh lapisan HTTP dan tidak pernah melihat apa yang terjadi di bawahnya. Akibatnya, begitu muncul bug aneh — koneksi putus di tengah transfer, data terpotong, atau server tiba-tiba hang — kamu akan kebingungan mencari penyebabnya. Memahami socket bikin kamu ngerti kenapa recv() bisa mengembalikan data yang belum utuh, kenapa koneksi bisa half-open, dan apa sebenarnya yang dilakukan connect() di balik layar. Modul socket sudah ada sejak Python 1, jadi tidak ada yang perlu kamu install.
Prasyarat
Python 3.8 ke atas sudah cukup. Semua kode di bawah memakai modul bawaan socket dan threading, jadi tidak ada pip install. Saya pakai 127.0.0.1 (localhost) supaya server dan client bisa dijalankan di komputer yang sama. Kalau kamu mau menguji lewat dua mesin, ganti HOST server jadi 0.0.0.0 dan arahkan client ke IP server.
IP, Port, dan Dua Peran dalam Koneksi
Socket adalah endpoint komunikasi yang punya alamat IP dan port. IP menentukan mesin mana, port menentukan proses mana di mesin itu — ibaratnya IP alamat gedung dan port nomor unit. Sebuah koneksi TCP selalu melibatkan dua peran: server yang memanggil listen() untuk menunggu koneksi masuk, dan client yang memanggil connect() untuk menghubungi server. Setelah terhubung, keduanya bisa saling kirim data dua arah lewat recv() dan sendall().
TCP vs UDP: TCP (SOCK_STREAM) menjamin data sampai utuh dan urut, tapi butuh handshake tiga langkah dulu. UDP (SOCK_DGRAM) lebih cepat tanpa jaminan, cocok untuk streaming atau game. Chat dan HTTP memakai TCP, jadi tutorial ini fokus ke SOCK_STREAM.
Echo Server: Versi Paling Dasar
Mulai dari yang paling sederhana: echo server. Server menerima koneksi, membaca apa pun yang dikirim client, lalu mengirim balik data yang sama. Ini template dasar hampir semua server TCP, dan cuma butuh sekitar 15 baris.
import socket
HOST = "127.0.0.1"
PORT = 8000
with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as server:
server.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
server.bind((HOST, PORT))
server.listen()
print(f"Server mendengarkan di {HOST}:{PORT}")
conn, addr = server.accept()
with conn:
print(f"Koneksi dari {addr}")
while True:
data = conn.recv(1024)
if not data:
break
conn.sendall(data)
Baris setsockopt(SOL_SOCKET, SO_REUSEADDR, 1) mencegah error Address already in use saat server di-restart cepat-cepat. Fungsi accept() memblokir sampai ada client yang connect, lalu mengembalikan dua hal: objek koneksi conn dan alamat client addr. Loop while True membaca pakai recv(1024) — angka 1024 adalah ukuran buffer maksimal dalam byte. Kalau recv() mengembalikan b'' (string kosong), artinya client sudah menutup koneksi, jadi kita berhenti.
import socket
HOST = "127.0.0.1"
PORT = 8000
with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as client:
client.connect((HOST, PORT))
client.sendall(b"Halo dari client!")
data = client.recv(1024)
print(f"Balasan server: {data.decode()}")
Jalankan server di satu terminal, client di terminal lain. Client mengirim b"Halo dari client!", server membalas dengan byte yang sama, dan client mencetaknya. Selama server masih berjalan, kamu bisa menjalankan client berkali-kali.
# terminal 1
python server.py
# terminal 2
python client.py
Jangan asumsikan recv(1024) mengembalikan satu pesan utuh. TCP adalah aliran byte (stream), bukan paket pesan — data bisa datang terpotong, atau beberapa pesan tergabung dalam satu panggilan recv(). Untuk chat sederhana ini tidak masalah, tapi kalau kamu mengirim payload besar, tandai batas pesan dengan panjang data atau delimiter seperti newline.
Menangani Banyak Client dengan Thread
Echo server di atas cuma bisa melayani satu client. Client kedua harus antre sampai yang pertama menutup koneksi, karena accept() dan recv() sama-sama memblokir. Solusinya: jalankan tiap koneksi di thread sendiri, supaya satu client yang diam tidak menghambat client lain.
import socket
import threading
HOST = "127.0.0.1"
PORT = 8000
def handle_client(conn: socket.socket, addr):
print(f"[+] {addr} terhubung")
with conn:
while True:
data = conn.recv(1024)
if not data:
break
conn.sendall(data)
print(f"[-] {addr} putus")
with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as server:
server.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
server.bind((HOST, PORT))
server.listen()
print(f"Server echo aktif di {HOST}:{PORT}")
while True:
conn, addr = server.accept()
threading.Thread(target=handle_client, args=(conn, addr), daemon=True).start()
Perubahan intinya cuma satu: hasil accept() tidak lagi diproses langsung, tapi dilempar ke Thread baru lewat handle_client(). Kalau kamu belum paham threading, intinya tiap koneksi mendapat jalur eksekusi sendiri sehingga recv() yang memblokir tidak menghentikan server menerima client baru. daemon=True membuat thread ikut mati saat program utama keluar.
Chat Server: Broadcast ke Semua Client
Sekarang naik level: server yang menyiarkan pesan dari satu client ke semua client lain. Bedanya dengan echo server, kita simpan semua koneksi aktif dalam satu list dan kirim pesan ke semuanya kecuali si pengirim. Kita juga pakai Lock supaya list clients tidak dimodifikasi dua thread secara bersamaan.
import socket
import threading
HOST = "127.0.0.1"
PORT = 8000
clients = []
lock = threading.Lock()
def broadcast(message: bytes, sender: socket.socket):
with lock:
for c in list(clients):
if c is not sender:
try:
c.sendall(message)
except OSError:
clients.remove(c)
def handle_client(conn: socket.socket, addr):
print(f"[+] {addr} terhubung")
with conn:
while True:
try:
data = conn.recv(1024)
except OSError:
break
if not data:
break
message = data.decode()
print(f"{addr}: {message}")
broadcast(f"{addr}: {message}".encode(), conn)
with lock:
if conn in clients:
clients.remove(conn)
print(f"[-] {addr} putus")
with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as server:
server.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
server.bind((HOST, PORT))
server.listen()
print(f"Server chat aktif di {HOST}:{PORT}")
while True:
conn, addr = server.accept()
with lock:
clients.append(conn)
threading.Thread(target=handle_client, args=(conn, addr), daemon=True).start()
import socket
import threading
HOST = "127.0.0.1"
PORT = 8000
def receive(client: socket.socket):
while True:
try:
data = client.recv(1024)
except OSError:
break
if not data:
print("\n[server menutup koneksi]")
break
print(f"\n{data.decode()}")
with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as client:
client.connect((HOST, PORT))
print("Terhubung. Ketik pesan, atau 'quit' untuk keluar.")
threading.Thread(target=receive, args=(client,), daemon=True).start()
while True:
message = input()
if message.strip().lower() == "quit":
break
client.sendall(message.encode())
Buka tiga terminal: satu untuk server, dua untuk client. Pesan yang diketik di client A muncul di client B, lengkap dengan alamat pengirimnya. Thread receive() berjalan di latar belakang supaya client tetap bisa membaca input() sambil menerima pesan masuk.
Versi chat ini memakai satu thread per koneksi. Untuk puluhan client itu oke, tapi ribuan thread akan memberatkan. Kalau kamu butuh ribuan koneksi sekaligus, pindah ke model non-blocking dengan asyncio atau select — satu thread bisa mengawasi banyak socket sekaligus.
Langkah Berikutnya
Server TCP yang kamu tulis barusan adalah fondasi hampir semua komunikasi jaringan. Untuk aplikasi web yang butuh komunikasi real-time dua arah dari browser, WebSocket menyediakan lapisan yang lebih tinggi di atas TCP dengan standar browser. Kalau kamu penasaran bagaimana protokol seperti SSH meneruskan koneksi antar mesin, baca tutorial SSH dan port forwarding — konsep socket yang sama, dipakai untuk tunneling dan remote access.