Pendalaman Ekstensif: Client-Server, Socket API, Timeouts, dan Eksekusi Kode Python
Referensi: Bahan Ajar Week11.pptx & RPS Jaringan Komputer
Dosen Pengampu:
Pembahasan Application Layer mencakup beberapa sub-topik mendasar yang menggerakkan internet:

Hampir sebagian besar layanan internet dikonstruksi menggunakan paradigma Client-Server.

Berbeda dengan arsitektur tersentralisasi, P2P tidak mengandalkan server utama yang selalu menyala.

Goal Utama: Mempelajari bagaimana membangun aplikasi klien dan server yang berkomunikasi melalui jaringan.
Definisi: Socket adalah sebuah Pintu (Door) atau titik akhir komunikasi antara proses aplikasi di user-space dengan protokol transport end-to-end di kernel OS (Operating System).
Proses aplikasi menjejalkan pesan ke luar pintu (Socket), dengan asumsi bahwa infrastruktur di balik pintu tersebut akan mengirimkannya ke Socket tujuan dengan selamat.

Siapa yang mengontrol Socket?
Aplikasi butuh memesan (request) sepasang socket ke OS. Satu untuk Client, satu untuk Server.

Sistem Operasi menyediakan dua jenis transport service utama yang menentukan tipe Socket:

Untuk mendemonstrasikan kedua tipe Socket, kita akan membangun aplikasi sederhana dengan alur:

Pada UDP, TIDAK ADA KONEKSI yang dibangun antara Klien dan Server sebelum transmisi data (No handshaking).

Alur siklus hidup Socket UDP:

from socket import *
serverName = 'hostname_atau_IP'
serverPort = 12000
# 1. Membuat UDP Socket (SOCK_DGRAM)
clientSocket = socket(AF_INET, SOCK_DGRAM)
message = input('Input lowercase sentence:')
# 2. Mengirim data beserta tujuan (serverName, Port)
clientSocket.sendto(message.encode(), (serverName, serverPort))
# 3. Menunggu balasan (recvfrom membaca data & alamat pengirim)
modifiedMessage, serverAddress = clientSocket.recvfrom(2048)
print(modifiedMessage.decode())
# 4. Tutup socket
clientSocket.close()

from socket import *
serverPort = 12000
serverSocket = socket(AF_INET, SOCK_DGRAM)
# 1. Mengikat (Bind) socket ke local port 12000
serverSocket.bind(('', serverPort))
print('The server is ready to receive')
# 2. Infinite Loop untuk melayani request tiada henti
while True:
# Menerima request dan menyimpan alamat IP Klien
message, clientAddress = serverSocket.recvfrom(2048)
modifiedMessage = message.decode().upper()
# Mengirim balasan ke IP Klien spesifik tersebut
serverSocket.sendto(modifiedMessage.encode(), clientAddress)
TCP bekerja bagaikan sambungan telepon. Harus diangkat (dihubungkan) terlebih dahulu sebelum bisa bicara.

Ada perbedaan mendasar di sisi Server pada TCP:
accept().accept() akan mencetak Socket Baru (Connection Socket) yang didedikasikan KHUSUS untuk berbicara 1-on-1 dengan Klien tersebut.
Alur siklus hidup Socket TCP:
serverSocket dan memanggil listen(). Tertidur menunggu di accept().connect() (memicu TCP Handshake).accept() menghasilkan connectionSocket baru.send() dan recv() tanpa perlu menempelkan IP tujuan lagi, karena pipa sudah terbentuk secara logikal.
from socket import *
serverName = 'servername'
serverPort = 12000
# 1. Membuat TCP Socket (SOCK_STREAM)
clientSocket = socket(AF_INET, SOCK_STREAM)
# 2. Inisiasi Koneksi 3-way Handshake
clientSocket.connect((serverName, serverPort))
sentence = input('Input lowercase sentence:')
# 3. Mengirim data langsung ke dalam "pipa", tanpa target IP lagi
clientSocket.send(sentence.encode())
# 4. Menerima balasan
modifiedSentence = clientSocket.recv(1024)
print('From Server:', modifiedSentence.decode())
clientSocket.close()

from socket import *
serverPort = 12000
serverSocket = socket(AF_INET, SOCK_STREAM)
serverSocket.bind(('', serverPort))
# 1. Mulai mendengarkan (Welcoming Socket)
serverSocket.listen(1)
print('The server is ready to receive')
while True:
# 2. Block/Tertidur sampai ada Klien masuk.
# Menghasilkan Socket BARU (connectionSocket) khusus klien ini.
connectionSocket, addr = serverSocket.accept()
# 3. Menerima data stream dari klien spesifik tersebut
sentence = connectionSocket.recv(1024).decode()
capitalizedSentence = sentence.upper()
# 4. Kirim balasan dan TUTUP connectionSocket klien ini.
# (serverSocket pembuka tamu TIDAK DITUTUP!)
connectionSocket.send(capitalizedSentence.encode())
connectionSocket.close()
Seringkali aplikasi jaringan harus menunggu berbagai kejadian (event), misalnya:
Fungsi seperti recv() bersifat Blocking. Jika tidak ada balasan dari seberang, program akan *freeze* selamanya. Solusinya adalah fitur Timeouts.

Di Python, kita dapat memasang "bom waktu" pada soket dengan perintah s.settimeout(10).
s.recv() tidak menerima byte apa pun, Python akan melemparkan sebuah interupsi Timeout Exception.
Bagaimana cara Python menangkap lemparan interupsi tersebut agar program tidak "crash"?
try:
# Blok kode yang rawan bermasalah / melempar exception
pesan = clientSocket.recv(1024)
print(pesan)
except timeout:
# Apa yang dilakukan jika terbukti Waktu Habis?
print("Server diam saja! Waktu tunggu 10 detik habis.")
except Exception as e:
# Penanganan error lainnya
print("Terjadi error jaringan: ", str(e))

Analoginya: Anak gembala (Klien) iseng terkoneksi ke Warga (Server) namun diam saja.

counter = 0
while counter < 3:
connectionSocket, addr = serverSocket.accept()
# Memasang "bom waktu" 10 detik khusus untuk koneksi anak gembala ini
connectionSocket.settimeout(10)
try:
wolf_location = connectionSocket.recv(1024).decode()
send_hunter(wolf_location) # Fungsi eksternal
connectionSocket.send('hunter sent')
except timeout:
# Jika anak gembala diam saja > 10 detik
counter += 1
print("Bocah iseng ketahuan!")
connectionSocket.close()
Mari kita bedah secara akademis perintah-perintah bawaan Python.
Fungsi Penciptaan: socket.socket(family, type)
family: Protokol apa yang dipakai?
AF_INET untuk Internet IPv4.AF_UNIX untuk komunikasi internal OS (Unix Domain).type: Jenis layanannya?
SOCK_STREAM untuk TCP.SOCK_DGRAM untuk UDP.
Fungsi s.connect(address) digunakan oleh Klien untuk melakukan inisiasi.
address pada protokol Internet (AF_INET) berbentuk **Tuple**, yakni pasangan (host, port).s.connect(("192.168.1.5", 8080))."/tmp/my_socket").
Fungsi ini dieksekusi oleh Server.
s.bind(address): Mengikat socket ke sebuah port fisik mesin tersebut agar OS tahu bahwa traffic di port tersebut adalah milik program kita. Address berupa Tuple (host, port). Host bisa diisi string kosong '' yang berarti mendengarkan semua kartu jaringan yang ada di mesin server.s.listen(queue): Mengubah socket menjadi pendengar (Welcoming Socket). Nilai queue (contoh: 1 atau 5) merepresentasikan maksimum antrian koneksi Klien di lobi yang menunggu di-accept oleh server.
s.accept(): Menahan laju program (blocking) sampai Klien terhubung. Mengembalikan nilai Tuple baru: (connectionSocket, addressKlien).s.send(bytes) (TCP) / s.sendto(bytes, address) (UDP): Mengirim urutan byte ke jaringan. Pastikan String diubah ke byte array via .encode()!s.recv(bufsize) (TCP) / s.recvfrom(bufsize) (UDP): Menerima maksimum ukuran penyangga (misal 1024 atau 2048 bytes) ke dalam RAM.
Setelah menguasai Socket, kita telah menutup bab Application Layer dengan sempurna. Hal esensial yang kita pelajari:
"Complexity at the network edge!"







