Socket #

Hampir semua komunikasi jaringan — HTTP, database, email, chat — dibangun di atas socket. Socket adalah abstraksi yang mewakili titik ujung sebuah koneksi jaringan: kamu bisa membuka socket, mengirim data ke dalamnya, dan membaca data yang datang darinya, persis seperti membaca dan menulis ke file. Python menyediakan modul socket yang memberi akses langsung ke API socket sistem operasi, memungkinkan kamu membangun server dan klien jaringan dari tingkat paling dasar.

Cara Kerja Socket #

Sebelum menulis kode, penting memahami lifecycle socket dan perbedaan peran antara server dan klien.

LIFECYCLE TCP (Server)                 LIFECYCLE TCP (Klien)
──────────────────────                 ─────────────────────
socket()  ← buat socket               socket()  ← buat socket
   │                                       │
bind()    ← ikat ke IP:port            connect() ← hubungkan ke server
   │                                       │
listen()  ← mulai mendengarkan         send()    ← kirim data
   │                                       │
accept()  ← terima koneksi klien       recv()    ← terima data
   │                                       │
recv()    ← baca data dari klien       close()   ← tutup koneksi
   │
send()    ← kirim respons
   │
close()   ← tutup koneksi klien

Dua protokol utama yang bekerja di atas socket:

TCP (SOCK_STREAM)          UDP (SOCK_DGRAM)
─────────────────          ────────────────
Koneksi persisten          Connectionless (tanpa koneksi)
Urutan data terjaga        Urutan tidak dijamin
Ada acknowledgment         Tidak ada acknowledgment
Lebih lambat               Lebih cepat
Cocok: HTTP, database,     Cocok: streaming video, game
       file transfer               online, DNS

TCP Socket #

TCP menjamin data sampai dengan urutan yang benar. Server TCP menunggu koneksi dari klien, lalu keduanya bisa saling berkirim data hingga salah satu menutup koneksi.

Server TCP Dasar #

import socket

def jalankan_server(host="127.0.0.1", port=65432):
    # AF_INET = IPv4, SOCK_STREAM = TCP
    with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as server:
        # SO_REUSEADDR: izinkan reuse port setelah server di-restart
        # tanpa ini, restart akan gagal dengan "Address already in use"
        server.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
        server.bind((host, port))
        server.listen(5)  # antrian maksimal 5 koneksi
        print(f"Server TCP berjalan di {host}:{port}")

        while True:
            conn, alamat = server.accept()
            print(f"Koneksi dari: {alamat}")
            tangani_klien(conn, alamat)

def tangani_klien(conn, alamat):
    with conn:
        while True:
            data = conn.recv(1024)  # baca maksimal 1024 byte
            if not data:
                break  # klien menutup koneksi
            pesan = data.decode("utf-8")
            print(f"[{alamat}] Terima: {pesan}")
            balasan = f"Echo: {pesan}"
            conn.sendall(balasan.encode("utf-8"))
        print(f"Koneksi dari {alamat} ditutup.")

if __name__ == "__main__":
    jalankan_server()

Klien TCP #

import socket

def jalankan_klien(host="127.0.0.1", port=65432):
    with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as klien:
        klien.connect((host, port))
        print(f"Terhubung ke {host}:{port}")

        pesan_list = ["Halo server!", "Ini pesan kedua", "Pesan terakhir"]
        for pesan in pesan_list:
            klien.sendall(pesan.encode("utf-8"))
            data = klien.recv(1024)
            print(f"Respons server: {data.decode('utf-8')}")

if __name__ == "__main__":
    jalankan_klien()
SO_REUSEADDR wajib di server. Tanpa opsi ini, setelah server dihentikan, port akan tetap dalam status TIME_WAIT selama beberapa menit. Saat kamu mencoba menjalankan server lagi, akan muncul error OSError: [Errno 98] Address already in use. Selalu tambahkan setsockopt(SOL_SOCKET, SO_REUSEADDR, 1) sebelum bind().

Server TCP Multi-Klien #

Server di atas hanya bisa melayani satu klien sekaligus — klien kedua harus menunggu hingga klien pertama selesai. Ini adalah anti-pattern yang paling umum dalam pemrograman socket.

# ANTI-PATTERN: server single-threaded
# klien kedua diblokir sampai klien pertama selesai
while True:
    conn, alamat = server.accept()
    tangani_klien(conn, alamat)  # blokir di sini sampai selesai

# BENAR: spawn thread baru untuk setiap klien
import threading

while True:
    conn, alamat = server.accept()
    thread = threading.Thread(target=tangani_klien, args=(conn, alamat))
    thread.daemon = True  # thread mati saat program utama keluar
    thread.start()

Implementasi lengkap server TCP multi-klien:

import socket
import threading

klien_aktif = {}
lock = threading.Lock()

def tangani_klien(conn, alamat):
    with lock:
        klien_aktif[alamat] = conn
    print(f"[+] Klien baru: {alamat} | Total: {len(klien_aktif)}")

    try:
        with conn:
            while True:
                data = conn.recv(1024)
                if not data:
                    break
                pesan = data.decode("utf-8")
                print(f"[{alamat}] {pesan}")
                conn.sendall(f"Echo: {pesan}".encode("utf-8"))
    except ConnectionResetError:
        print(f"[!] Klien {alamat} terputus paksa.")
    finally:
        with lock:
            klien_aktif.pop(alamat, None)
        print(f"[-] Klien {alamat} keluar | Sisa: {len(klien_aktif)}")

def jalankan_server(host="127.0.0.1", port=65432):
    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 multi-klien berjalan di {host}:{port}")

        while True:
            conn, alamat = server.accept()
            thread = threading.Thread(target=tangani_klien, args=(conn, alamat))
            thread.daemon = True
            thread.start()

if __name__ == "__main__":
    jalankan_server()

Timeout dan Penanganan Error #

Koneksi jaringan bisa tergantung tanpa batas — klien mogok, jaringan putus, atau server tidak merespons. Tanpa timeout, program bisa hang selamanya.

import socket

def klien_dengan_timeout(host="127.0.0.1", port=65432):
    klien = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    klien.settimeout(5.0)  # timeout 5 detik untuk operasi connect, recv, send

    try:
        klien.connect((host, port))
        klien.sendall("Halo!".encode("utf-8"))

        data = klien.recv(1024)
        print(f"Respons: {data.decode('utf-8')}")

    except socket.timeout:
        print("Koneksi timeout — server tidak merespons dalam 5 detik.")
    except ConnectionRefusedError:
        print("Koneksi ditolak — server tidak berjalan atau port salah.")
    except OSError as e:
        print(f"Error jaringan: {e}")
    finally:
        klien.close()

klien_dengan_timeout()

Timeout juga bisa diset di sisi server agar koneksi klien yang idle tidak menggantung resource:

def tangani_klien_dengan_timeout(conn, alamat):
    conn.settimeout(30.0)  # klien harus kirim data dalam 30 detik
    with conn:
        try:
            while True:
                data = conn.recv(1024)
                if not data:
                    break
                conn.sendall(data)
        except socket.timeout:
            print(f"[!] Klien {alamat} idle terlalu lama, koneksi ditutup.")

UDP Socket #

UDP tidak membangun koneksi — server langsung menerima paket dari siapa saja, dan klien langsung mengirim tanpa handshake terlebih dahulu. Lebih cepat, tapi tidak ada jaminan data sampai.

Server UDP #

import socket

def server_udp(host="127.0.0.1", port=65433):
    # SOCK_DGRAM = UDP
    with socket.socket(socket.AF_INET, socket.SOCK_DGRAM) as server:
        server.bind((host, port))
        print(f"Server UDP berjalan di {host}:{port}")

        while True:
            # recvfrom: terima data + alamat pengirim sekaligus
            data, alamat_pengirim = server.recvfrom(1024)
            pesan = data.decode("utf-8")
            print(f"[UDP] Dari {alamat_pengirim}: {pesan}")

            # sendto: kirim ke alamat spesifik (tidak perlu koneksi)
            balasan = f"Diterima: {pesan}"
            server.sendto(balasan.encode("utf-8"), alamat_pengirim)

if __name__ == "__main__":
    server_udp()

Klien UDP #

import socket

def klien_udp(host="127.0.0.1", port=65433):
    with socket.socket(socket.AF_INET, socket.SOCK_DGRAM) as klien:
        klien.settimeout(3.0)
        alamat_server = (host, port)

        pesan_list = ["Paket 1", "Paket 2", "Paket 3"]
        for pesan in pesan_list:
            klien.sendto(pesan.encode("utf-8"), alamat_server)
            try:
                data, _ = klien.recvfrom(1024)
                print(f"Respons: {data.decode('utf-8')}")
            except socket.timeout:
                print(f"Timeout — paket '{pesan}' mungkin hilang.")

if __name__ == "__main__":
    klien_udp()

Kapan Memilih TCP vs UDP #

Gunakan TCP jika:
  ✓ Data harus sampai lengkap dan berurutan
  ✓ Implementasi protokol aplikasi: HTTP, FTP, SSH, database
  ✓ Transfer file atau dokumen
  ✓ Komunikasi chat atau API

Gunakan UDP jika:
  ✓ Kecepatan lebih penting dari keandalan
  ✓ Kehilangan sedikit paket dapat ditoleransi
  ✓ Streaming audio/video real-time
  ✓ Game online (posisi pemain, status frame)
  ✓ DNS query (paket kecil, sekali kirim)
  ✓ Broadcasting ke banyak penerima sekaligus

Ringkasan #

  • Socket adalah abstraksi titik ujung koneksi jaringan — kamu membuka, mengirim, menerima, dan menutupnya seperti file.
  • Selalu tambahkan SO_REUSEADDR sebelum bind() di server agar port bisa langsung dipakai kembali setelah restart.
  • Server single-threaded adalah anti-pattern — klien kedua akan diblokir; gunakan threading.Thread atau ThreadPoolExecutor untuk melayani banyak klien secara bersamaan.
  • Selalu set timeout dengan socket.settimeout() — tanpanya, program bisa hang selamanya saat jaringan bermasalah.
  • TCP untuk komunikasi yang membutuhkan keandalan dan urutan data; UDP untuk kecepatan saat sedikit kehilangan paket bisa ditoleransi.
  • Gunakan with socket.socket(...) as s: agar socket selalu ditutup otomatis meski terjadi error.
  • Data socket adalah bytes, bukan string — selalu .encode() saat mengirim dan .decode() saat menerima.

← Sebelumnya: Decorator   Berikutnya: Web Socket →

About | Author | Content Scope | Editorial Policy | Privacy Policy | Disclaimer | Contact