Pengembangan aplikasi jaringan memerlukan teknik testing yang berbeda dari pengembangan aplikasi biasa. Bug jaringan sering hanya muncul di kondisi tertentu—koneksi yang ditolak, paket yang hilang, latensi tinggi, atau respons yang tidak sesuai format. Bab ini memperkenalkan tiga alat fundamental untuk testing aplikasi jaringan: netcat (nc) untuk pengujian manual, tcpdump/wireshark untuk capture paket, dan klien C kustom sebagai pengganti nc ketika tidak tersedia. Kita juga akan melihat ss -tn untuk inspeksi koneksi aktif dan openssl s_client untuk debugging TLS.
Tiga Tingkat Testing Aplikasi Jaringan #
| Tingkat | Alat | Yang Diuji |
|---|---|---|
| Manual | nc, telnet, klien C kustom | Protokol aplikasi dari sisi user |
| Capture | tcpdump, wireshark, ss | Paket aktual di kabel |
| Integration | Unit test dengan mock server | Logika bisnis aplikasi |
Tabel di atas adalah pendekatan berlapis. Tingkat manual cepat untuk debugging awal—kita bisa ketik perintah dan lihat respons. Tingkat capture menunjukkan apa yang sebenarnya terjadi di kabel (termasuk header TCP/IP yang tidak terlihat di aplikasi). Tingkat integration mengotomatiskan pengujian untuk CI/CD pipeline.
nc: Pengujian Manual Protokol Teks #
nc (netcat) adalah alat serbaguna untuk membaca dan menulis data ke koneksi TCP/UDP. Berikut cara menguji echo server dari bab Client-Server dengan nc:
# Terminal 1: jalankan echo server
./cs_server &
# Terminal 2: jalankan nc sebagai klien
nc 127.0.0.1 9093
PING
PING
echo ini teks
echo ini teks
QUIT
QUIT
^DSetiap baris yang kita ketik dikirim ke server, server echo balik, dan kita melihat responsnya. ^D (Ctrl+D) menutup stdin yang mengirim EOF ke nc, yang pada gilirannya menutup koneksi TCP.
Pada beberapa sistem, nc tidak terinstall. Sebagai alternatif, kita bisa menulis klien C sederhana yang berfungsi seperti nc—membaca stdin, mengirim ke socket, dan menampilkan respons ke stdout. Berikut implementasinya:
/* File: rawtcp.c - Klien TCP sederhana (pengganti nc untuk testing) */
#define _POSIX_C_SOURCE 200809L
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>
#include <sys/select.h>
#include <sys/socket.h>
#include <netdb.h>
/* Penggunaan: ./rawtcp <host> <port>
* Membaca stdin, kirim ke server, cetak respons ke stdout
*/
int main(int argc, char **argv) {
if (argc < 3) {
fprintf(stderr, "Penggunaan: %s <host> <port>\n", argv[0]);
return 1;
}
const char *host = argv[1];
const char *port = argv[2];
struct addrinfo hints = {0}, *res;
hints.ai_family = AF_INET;
hints.ai_socktype = SOCK_STREAM;
int rc = getaddrinfo(host, port, &hints, &res);
if (rc != 0) {
fprintf(stderr, "getaddrinfo: %s\n", gai_strerror(rc));
return 1;
}
int fd = socket(res->ai_family, res->ai_socktype, res->ai_protocol);
if (fd < 0) { perror("socket"); return 1; }
if (connect(fd, res->ai_addr, res->ai_addrlen) != 0) {
perror("connect"); close(fd); return 1;
}
freeaddrinfo(res);
fprintf(stderr, "[rawtcp] tersambung ke %s:%s\n", host, port);
/* Baca stdin -> kirim ke socket, baca socket -> cetak stdout */
char buf[1024];
fd_set rfds;
while (1) {
FD_ZERO(&rfds);
FD_SET(STDIN_FILENO, &rfds);
FD_SET(fd, &rfds);
int nfds = (fd > STDIN_FILENO ? fd : STDIN_FILENO) + 1;
if (select(nfds, &rfds, NULL, NULL, NULL) < 0) {
if (errno == EINTR) continue;
perror("select"); break;
}
if (FD_ISSET(STDIN_FILENO, &rfds)) {
ssize_t n = read(STDIN_FILENO, buf, sizeof(buf));
if (n <= 0) { shutdown(fd, SHUT_WR); }
else send(fd, buf, (size_t)n, 0);
}
if (FD_ISSET(fd, &rfds)) {
ssize_t n = recv(fd, buf, sizeof(buf), 0);
if (n <= 0) break;
fwrite(buf, 1, (size_t)n, stdout);
fflush(stdout);
}
}
close(fd);
return 0;
}Compile dan uji:
gcc -std=c11 -Wall -Wextra -o rawtcp rawtcp.c
./cs_server &
sleep 0.3
printf "PING\nraw-test-2\nQUIT\n" | ./rawtcp 127.0.0.1 9093
waitOutput yang Dihasilkan (stdout klien):
raw-test-1
raw-test-2
QUITOutput yang Dihasilkan (server):
[SERVER] echo server di 127.0.0.1:9093
[SERVER] klien terhubung
[SERVER] echo: raw-test-1
[SERVER] echo: raw-test-2
[SERVER] echo: QUIT
[SERVER] klien memutus (0)Mari kita telaah baris-baris kode di atas:
- Baris 36-37 (
select(nfds, &rfds, ...)): I/O multiplexing—selectmemblokir hingga salah satu dari stdin atau socket siap dibaca. Tanpaselect, kita harus memilih satu sumber: bila hanya loop baca stdin, kita tidak bisa melihat respons server tepat waktu. Denganselect, klien bisa menangani kedua arah secara bersamaan dalam satu thread. - Baris 41-45 (
FD_ISSET(STDIN_FILENO, ...)): Bila stdin siap dibaca, baca byte, lalusendke socket. Bilareadmengembalikan 0 (EOF, mis. Ctrl+D atau pipe tertutup), panggilshutdown(fd, SHUT_WR)untuk mengirim FIN ke server—memberitahu “saya selesai mengirim, tapi masih mau menerima”. - Baris 46-52 (
FD_ISSET(fd, ...)): Bila socket siap dibaca,recvdata lalu cetak ke stdout. Bilarecvmengembalikan0, server telah menutup koneksi—keluar dari loop. Bila-1, terjadi error—juga keluar. - Pola ini disebut “bidirectional relay”: aplikasi menghubungkan dua stream (stdin ke socket, socket ke stdout) tanpa buffering. Pola yang sama dipakai oleh nc, socat, dan banyak alat testing jaringan lain.
ss: Inspeksi Koneksi Aktif #
ss (socket statistics) adalah alat modern yang menggantikan netstat. Untuk melihat semua koneksi TCP aktif:
ss -tn | head -5Output yang Dihasilkan:
State Recv-Q Send-Q Local Address:Port Peer Address:Port
ESTAB 0 0 21.0.9.138:19006 21.0.0.1:52974
ESTAB 0 0 21.0.9.138:42946 10.243.55.100:3306
ESTAB 0 0 21.0.9.138:43366 10.243.55.100:3306
ESTAB 0 0 21.0.9.138:19005 21.0.0.1:49124Output di atas menunjukkan beberapa koneksi ESTAB (established). Kolom Local Address:Port adalah sisi koneksi di mesin ini, Peer Address:Port adalah sisi remote. Baris kedua dan ketiga menunjukkan koneksi ke 10.243.55.100:3306—port 3306 adalah MySQL default, jadi kemungkinan ini adalah koneksi database.
Untuk melihat socket yang listening saja (server yang menunggu koneksi):
ss -tlnp | head -6Output yang Dihasilkan:
State Recv-Q Send-Q Local Address:Port Peer Address:Port Process
LISTEN 0 2048 127.0.0.1:12600 0.0.0.0:*
LISTEN 0 128 127.0.0.1:19001 0.0.0.0:*
LISTEN 0 1024 0.0.0.0:19005 0.0.0.0:*
LISTEN 0 128 0.0.0.0:19006 0.0.0.0:*
LISTEN 0 4096 *:81 *:* ss berguna untuk memverifikasi bahwa server Anda benar-benar mendengarkan di port yang diharapkan, dan untuk debugging masalah “port already in use” (lihat baris dengan port yang sama—satu proses telah memegangnya).
tcpdump dan Wireshark: Capture Paket #
Untuk melihat paket aktual yang mengalir di kabel, tcpdump adalah alat command-line standar. wireshark adalah GUI yang lebih kaya fitur tetapi membutuhkan display server. Penggunaan dasar:
# Capture semua paket TCP di interface lo (loopback) port 9093
sudo tcpdump -i lo -n tcp port 9093Output khas (dipotong untuk kejelasan):
13:42:11.123456 IP 127.0.0.1.43210 > 127.0.0.1.9093: Flags [S], seq 123456, win 65495, [tcp sum ok], length 0
13:42:11.123457 IP 127.0.0.1.9093 > 127.0.0.1.43210: Flags [S.], seq 789012, ack 123457, win 65483, [tcp sum ok], length 0
13:42:11.123458 IP 127.0.0.1.43210 > 127.0.0.1.9093: Flags [.], ack 1, win 512, length 0
13:42:11.124001 IP 127.0.0.1.43210 > 127.0.0.1.9093: Flags [P.], seq 1:6, ack 1, win 512, length 5Empat baris pertama adalah TCP three-way handshake: klien mengirim SYN (Flags [S]), server membalas SYN-ACK (Flags [S.]), klien mengirim ACK (Flags [.]). Baris keempat menunjukkan klien mengirim data 5 byte (length 5) dengan flag PSH-ACK (Flags [P.]). Inilah yang sebenarnya terjadi di tingkat TCP ketika aplikasi memanggil connect() + send().
openssl s_client: Debug TLS #
Untuk menguji server HTTPS atau protokol berbasis TLS lainnya, openssl s_client adalah alat yang powerful. Alih-alih hanya konektivitas TCP, alat ini menampilkan seluruh certificate chain dan hasil verifikasi:
openssl s_client -connect www.example.com:443 -servername www.example.com </dev/null 2>/dev/null | head -20Output yang Dihasilkan:
Connecting to 172.66.147.243
depth=3 C=US, O=SSL Corporation, CN=SSL.com TLS ECC Root CA 2022
verify return:1
depth=2 C=US, O=SSL Corporation, CN=SSL.com TLS Transit ECC CA R2
verify return:1
depth=1 C=US, O=SSL Corporation, CN=Cloudflare TLS Issuing ECC CA 3
verify return:1
depth=0 CN=example.com
verify return:1
CONNECTED(00000003)
---
Certificate chain
0 s:CN=example.com
i:C=US, O=SSL Corporation, CN=Cloudflare TLS Issuing ECC CA 3Output di atas menampilkan certificate chain dari leaf certificate (CN=example.com) hingga root CA. verify return:1 di setiap depth menandakan verifikasi sukses pada level tersebut. Bila sertifikat kedaluwarsa, self-signed, atau hostname tidak cocok, verify return:0 akan muncul pada level yang bermasalah.
Strategi Testing Terstruktur #
Untuk aplikasi production, pengujian manual tidak cukup. Berikut strategi berlapis yang direkomendasikan:
- Unit test dengan mock server: tulis server mock yang merespons sesuai skenario, jalankan klien dalam test framework (mis.
cmocka,Criterion). Setiap skenario (sukses, timeout, koneksi ditolak) punya test case sendiri. - Integration test dengan container: jalankan server dan klien di container Docker terpisah, verifikasi end-to-end. Docker Compose cocok untuk ini.
- Chaos testing: simulasikan kegagangan jaringan dengan
tc(traffic control) atautoxiproxy—drop paket, tambahkan latensi, batasi bandwidth. - Fuzzing: kirim input acak atau malformed ke parser protokol (mis. dengan
libFuzzeratau `AFL++). Bab-bab di section debugging membahas fuzzing lebih dalam.