Linux'ta Network Sorunlarını Syscall Perspektifiyle Okumak
🇹🇷 Ping çalışıyor ama uygulama bağlanamıyor mu? Connection Refused ile Timeout arasındaki farkı, connect, sendto ve accept çağrıları üzerinden, kernel'ın gözünden anlatıyoruz.
Önceki yazımızda Linux’ta Dosya ve Yetki Problemlerini Kernel Seviyesinde Anlamak başlığı altında, dosya sisteminin (VFS) bürokrasisini ve “Permission Denied” hatalarını incelemiştik.
Peki ya dosyaya erişebiliyorsunuz ama bu sefer de dış dünyaya, internete veya veritabanına erişemiyorsanız?
Bir sistem yöneticisi (SysAdmin) veya DevOps mühendisi için en gergin, en saç baş yolduran anlardan biri, Network (Ağ) ekibiyle veya veri merkezi yöneticileriyle yapılan o soğuk savaştır.
Siz: “Uygulama sunucuya erişemiyor. Connection Timeout alıyorum. Firewall’da bir engelleme var mı?” Networkçü: “Hayır, erişim açık. Benim ekranımda her şey yeşil. Router logları temiz. Ping atıyorum cevap dönüyor. Sorun senin sunucuda veya kodunda.”
O an derin bir nefes alırsınız. Terminalinizdeki nmap veya curl çıktısındaki o inatçı Connection timed out hatasına bakarsınız. Sonra ping atarsınız ve cevap gelir. “Allah Allah…” dersiniz. “Ping gidiyor, telnet gitmiyor. Acaba sorun gerçekten bende mi? Yoksa networkçü beni başından mı savıyor?”
Bu yazıda, Network ekibini suçlamadan önce (veya suçlayabilmek için elinizde reddedilemez teknik kanıtlar toplarken) kullanacağımız “cerrahi aletleri” masaya yatıracağız.
📌 Bu Yazıda Ne Öğreneceksin?
Bu yazı, ezbere komutlar listesi değil, ağın “ruhunu” anlama rehberidir. Okumayı bitirdiğinizde:
- Telefon Santrali Metaforu:
socket,bind,listen,acceptveconnectsyscall’larının aslında nasıl bir “Sandalye Kapmaca” veya “Restoran Sırası” yönettiğini göreceksiniz. - Connection Refused vs Timeout: İkisi arasındaki farkın, sorunun kaynağını (Server mı, Network mü?) şıp diye bulmanızı nasıl sağladığını anlayacaksınız.
- DNS’in Sırları:
straceçıktısında neden bazen UDP yerine UNIX Domain Socket (nscd) gördüğünüzü ve DNS’in TCP bağlantılarını başlamadan nasıl öldürdüğünü öğreneceksiniz. - Backlog & SYN Flood: Sunucunuz ayaktayken neden “Bağlanamıyorum” dendiğini, Backlog (Restoran Hostesi) analojisiyle beyninize kazıyacaksınız.
- Gerçek Hayat Tuzakları: Kubernetes (
ndots:5), Docker (localhostdöngüsü) ve Java/Node.js/Python dünyasındaki hata mesajlarının Kernelca çevirisini cebinize koyacaksınız.
Kısacası; OSI Modelini ezbere saymak yerine, elinizi kirletip Kernel’ın ağ trafiğini nasıl yönettiğini ve paketlerin neden kaybolduğunu canlı izlemeyi öğreneceksiniz.
📞 1. Telefon Santrali Metaforu: Socket Dünyası
Linux’ta ağ iletişimi (Network I/O), dosya I/O’sına şaşırtıcı derecede benzer. UNIX felsefesini hatırlayın: “Linux’ta her şey bir dosyadır.” Ağ soketleri (sockets) de, üzerine yazıp okuyabileceğiniz özel birer dosyadır.
Bir TCP bağlantısının kurulma sürecini (Three-Way Handshake), eski usul bir telefon görüşmesine benzetirsek taşlar yerine oturacaktır:
sequenceDiagram
participant Client as Arayan (Client)
participant Cloud as Şebeke (Network)
participant Server as Santral (Server)
Note over Server: 1. socket() + bind() + listen()<br/>(Telefonu fişe tak, hattı aç)
Note over Client: 2. socket() + connect()<br/>(Numarayı çevir)
Client->>Cloud: [SYN] "Alo? Orada kimse var mı?"
activate Cloud
Cloud->>Server: (Telefon Çalar...)
deactivate Cloud
activate Server
Server-->>Cloud: [SYN-ACK] "Evet, ben buradayım!"
deactivate Server
activate Cloud
Cloud-->>Client: (Çalma sesi durur, hat açılır)
deactivate Cloud
Client->>Cloud: [ACK] "Tamam, seni duyuyorum."
activate Cloud
Cloud->>Server: (Bağlantı Onayı İletilir)
deactivate Cloud
Note right of Server: 3. accept() Döndü!<br/>(Ahizeyi kaldır)
Note over Client, Server: Bağlantı Kuruldu (ESTABLISHED)
Client->>Server: "GET /index.html" (Veri Gönderimi)
A. Sunucu (Server) Tarafı: “Santrali Kurmak”
socket()(Telefonu Satın Alma): İşlem yapmak için önce fiziksel bir cihaza ihtiyacınız var. Kodunuzdasocket()fonksiyonunu çağırdığınızda, Kernel’a şunu dersiniz: “Bana IPv4 dilini konuşan, TCP özellikli bir uç nokta ver.” Kernel size bir “File Descriptor” (Dosya Numarası, örneğin: 3) verir. Henüz bir numaranız yok, hattınız bağlı değil. Sadece elinizde bir telefon makinesi var.🧐 Teknik Detay: File Descriptor (FD) Nedir?
Linux’ta açılan her dosya, soket veya pipe için Kernel bir tam sayı (integer) atar.
0: Standard Input (stdin)1: Standard Output (stdout)2: Standard Error (stderr) Bu yüzden programınızın açtığı ilk soket veya dosya genellikle3numarasını alır.socket() = 3çıktısının sırrı budur.
bind()(Sandalye Kapmaca): Diğer insanların sizi bulabilmesi için sabit bir yere oturmanız lazım. “Ben 192.168.1.5 masasındaki 80 numaralı sandalyeye oturacağım” dersiniz. Ancak, eğer o sandalyede başka bir uygulama oturuyorsa (örn: Nginx çalışırken Apache açmaya çalışmak), Kernel “Oturamazsın!” der veEADDRINUSE(Address already in use) hatasını fırlatır. Bu, Kernel’ın oynattığı bir sandalye kapmaca oyunudur; bir portu (sandalyeyi) sadece tek bir kişi (process) kapabilir.listen()(Telefonu Açık Bırakma): Tesisat hazır. Şimdi santrali aktif ediyorsunuz. “Artık aramaları kabul etmeye hazırım.” Burada çok kritik, genelde gözden kaçan bir detay vardır: Backlog Queue. Kernel’a şunu söylersiniz: “Ben telefondayken arkada kaç kişi bekleyebilir?”. Bu sayı önemlidir (daha sonra değineceğiz).accept()(Ahizeyi Kaldırma): Telefon çaldığında (Client bağlandığında) sunucu uygulamanız ahizeyi kaldırır. Bu işlem, yepyeni bir soket (yeni bir hat) oluşturur. Ana hat (Listening Socket) meşgul edilmez, görüşme bu yeni hat üzerine aktarılır. Böylece ana hat, yeni gelen aramaları beklemeye devam edebilir.
B. İstemci (Client) Tarafı: “Arama Yapmak”
socket(): İstemci de önce bir telefon makinesi edinir.connect()(Numara Çevirme): Hedef numarayı (IP + Port) çevirir ve ahizeyi kulağına dayayıp beklemeye başlar. İşte bütün olay, bütün o timeout hataları, bütün o beklemeler burada, bu çağrı sırasında yaşanır.
Bu süreci anladığımızda, hataları yorumlamak çok daha kolaylaşır.
⛔ 2. Connection Refused vs Timeout: Hayati Fark
Yazılımcıların ve junior sistemcilerin en sık karıştırdığı, birbirinin yerine kullandığı iki kavram vardır. İkisi de “Bağlanamadım” demektir ama nedenleri birbirinden gece ile gündüz kadar farklıdır. Bu farkı anlamak, sorunun kaynağını (Server tarafı mı, yoksa aradaki Network mü?) bulmanın tek yoludur.
A. Connection Refused (ECONNREFUSED)
- Senaryo: Arkadaşınızı aradınız. Telefon çaldı. Arkadaşınız (veya sekreteri) telefonu açtı ve “Seninle konuşmak istemiyorum” diyip suratınıza kapattı. Veya operatörden “Aradığınız numara kullanılmamaktadır” mesajı aldınız.
- Teknik Anlamı: İstemci, sunucuya bir SYN paketi (Merhaba, konuşabilir miyiz?) gönderdi. Sunucu (ya da sunucunun hemen önündeki güvenlik duvarı), bir RST, ACK (Reset) paketiyle geri döndü. “HAYIR!” dedi.
- Ne Demek İstiyor?
- Ulaşım Tamam: Tebrikler! Hedef sunucuya fiziksel olarak ulaştınız. Network kabloları sağlam, IP routing doğru. Network ekibini suçlamayın.
- Servis Yok: O IP adresinde, o Port numarasını (
8080diyelim) dinleyen canlı bir program YOK. - Aktif Red: Veya sunucu üzerindeki yerel firewall (iptables/firewalld), “Bu paketi derhal reddet (REJECT)” kuralı işletiyor.
💡 Teşhis: Eğer
Connection Refusedalıyorsanız, Router/Switch yönetenleri rahatsız etmeyin. Sorun %99 ihtimalle hedef sunucuda. Ya servis çökmüş (crash), ya yanlış portta çalışıyor ya da servis127.0.0.1‘i dinlerken siz dış IP’den gelmeye çalışıyorsunuz.
B. Connection Timed Out (ETIMEDOUT)
- Senaryo: Arkadaşınızı aradınız… dıt… dıt… çalıyor… çalıyor… 30 saniye geçti… 1 dakika geçti… Açan yok. Telesekreter bile yok. Sadece derin bir sessizlik. Sonunda sıkılıp kapatıyorsunuz.
- Teknik Anlamı: İstemci SYN paketini (bazen defalarca) gönderdi. Ama karşıdan “ÇIT” çıkmadı. Ne “Olumlu” (SYN-ACK) ne de “Olumsuz” (RST/Refused) bir cevap geldi. Paket bir kara deliğe düştü.
- Ne Demek İstiyor?
- Fiziksel Kayıp: Paket yolda kayboldu. Kablo kopuk, router bozuk.
- DROP (En Yaygın): Aradaki bir Kurumsal Firewall veya sunucunun kendi güvenlik duvarı, paketi sessizce çöpe attı (DROP). “Seni muhatap bile almıyorum, reddettiğimi bile söylemeyeceğim” dedi.
- Yanlış IP/Routing: Paket yanlış bir yola girdi ve geri dönüş yolunu bulamadı.
💡 Teşhis: İşte şimdi Network ekibini arayabilirsiniz. “Paketim bir yerlerde düşüyor (Drop)” diyebilirsiniz. Veya sunucunun firewall kurallarında
REJECTyerineDROPkullanılıyordur.
🌍 Yazılımcı - Kernel Sözlüğü
Uygulamanızın dili Kernelca bilmez, kendi lehçesini konuşur. İşte çeviri tablosu:
| Kernel Hatası | Java (Spring/Tomcat) | Node.js (Axios/Express) | Python (Requests/Django) |
|---|---|---|---|
| ECONNREFUSED | java.net.ConnectException: Connection refused | Error: connect ECONNREFUSED 1.2.3.4:80 | requests.exceptions.ConnectionError: [Errno 111] Connection refused |
| ETIMEDOUT | java.net.SocketTimeoutException: connect timed out | Error: connect ETIMEDOUT | requests.exceptions.ConnectTimeout |
| EADDRINUSE | java.net.BindException: Address already in use | Error: listen EADDRINUSE: address already in use | OSError: [Errno 98] Address already in use |
Loglarınızda bu satırları gördüğünüzde, sorunun aslında Kernel’ın fırlattığı TCP hataları olduğunu hatırlayın.
🕵️♂️ 3. Vaka Analizi: “Ping Çalışıyor Ama Uygulama Çalışmıyor”
Bu, en klasik “Bilişim Cinayet Masası” vakasıdır.
Olay Yeri: Web sunucunuz (192.168.1.50) çalışıyor. Kendi laptopunuzdan ping 192.168.1.50 yapıyorsunuz, cevaplar ms cinsinden şakır şakır dönüyor. “Oh, hat sağlam” diyorsunuz. Tarayıcıyı açıp adresi giriyorsunuz… Dönüyor… Dönüyor… Ve Connection timed out.
Yanlış Teşhis: “Ping dönüyor, demek ki sunucuya erişim var. Sorun kesin tarayıcıda, cache’de veya benim kodumda.”
Doğru Teşhis: “Ping ICMP protokolüdür (Layer 3 - Network). Web ise TCP/80 protokolüdür (Layer 4 - Transport). Bu ikisi elma ile armut gibidir. Yollarının açık olması birbirini garanti etmez.”
Gelin bu durumu strace ile, uygulamanın (örneğin curl) gözünden izleyelim:
1
2
# -v: verbose modu, detayları görmek için
strace -e trace=connect,socket,poll,select curl -v http://192.168.1.50
Çıktıda şu dramı izlersiniz:
1
2
3
4
5
socket(AF_INET, SOCK_STREAM, IPPROTO_TCP) = 3
connect(3, {sa_family=AF_INET, sin_port=htons(80), sin_addr=inet_addr("192.168.1.50")}, 16) = -1 EINPROGRESS (Operation now in progress)
poll([{fd=3, events=POLLOUT|POLLWRBAND}], 1, 1000) = 0 (Timeout)
poll([{fd=3, events=POLLOUT|POLLWRBAND}], 1, 1000) = 0 (Timeout)
...
socket(): Telefon alındı (File Descriptor 3).SOCK_STREAMparametresi bize bunun TCP olduğunu söylüyor.connect(): Numarayı çevirdi. Ama cevap (Return Code0) hemen gelmedi. Kernel dedi ki:EINPROGRESS.- Teknik Detay: Modern istemciler “Non-blocking” soket kullanır. Yani
connectçağrısında program donmaz, Kernel “Tamam, ben arka planda (TCP Handshake) deniyorum, sen işine bak” der.
- Teknik Detay: Modern istemciler “Non-blocking” soket kullanır. Yani
poll(): Uygulama beklemeye başladı. “Bağlantı tamamlandı mı?” diye soruyor. 1 saniye geçiyor… Cevap yok. 5 saniye geçiyor… Cevap yok.- Sonunda uygulama pes eder ve “Timeout” hatasını basar.
Analiz: Ping (ICMP Echo Request) paketi, Port kavramı içermez. Sadece “O makine (IP) orada mı?” diye bakar ve kapıyı çalar. HTTP isteği (TCP Port 80) ise “O makinenin 80 numaralı kapısı açık mı?” diye bakar. Firewall kuralı (örneğin AWS Security Group veya iptables) muhtemelen şöyledir:
- Protocol: ICMP -> ALLOW (Ping’e izin ver)
- Protocol: TCP, Port: 80 -> BLOCK/DROP (Web trafiğini engelle)
Ders: Bağlantı testi için asla ve asla sadece
pingkullanmayın. Ping yanıltıcı bir dosttur. Port bazlı test yapın:
telnet <ip> <port>(Eski ama altın)nc -zv <ip> <port>(Netcat - Modern ve hızlı)curl -v telnet://<ip>:<port>(Curl ile TCP testi)“Portu test etmeyen test, test değildir.”
📒 4. DNS: Telefon Rehberi ve UDP
Bazen connect çağrısını bile göremezsiniz. Uygulama “donar”. Sanki numarayı çevirmeye çalışıyor ama eli havada kalmış gibidir.
1
2
3
import requests
# Kodu çalıştırıyorsunuz ve terminal burada 5-10 saniye donuyor
requests.get("http://api.internal-service.local")
Buna strace ile baktığınızda, beklediğiniz connect (TCP 80) çağrısını göremezsiniz. Onun yerine şunu görürsünüz:
1
2
3
4
socket(AF_INET, SOCK_DGRAM|SOCK_CLOEXEC, IPPROTO_IP) = 3
connect(3, {sa_family=AF_INET, sin_port=htons(53), sin_addr=inet_addr("8.8.8.8")}, 16) = 0
sendto(3, "...", ...)
poll([{fd=3, events=POLLIN}], 1, 5000) ...
Dikkat! SOCK_STREAM (TCP) değil SOCK_DGRAM (UDP) gördük. Ve Port 53.
Buradaki sorun “Arama (TCP)” değil, “Numarayı Bulma (UDP/DNS)” sorunudur. Bunu bir Telefon Rehberi analojisiyle düşünün:
- TCP (Web - Telefon Görüşmesi): Karşı tarafı arayıp “Alo” dersiniz, o da “Efendim” der (Handshake). Bağlantı süreklidir.
- UDP (DNS - Posta): Rehber servisine bir kağıt fırlatırsınız: “Google’ın numarası kaç?” (Fire-and-Forget). Cevap gelmesini beklersiniz.
Eğer o kağıt (UDP paketi) yolda rüzgardan uçarsa veya rehber servisi (8.8.8.8) kağıdı alıp cevap vermezse, kimse size “Hata oluştu” demez. Telefon ahizesinden “dıt dıt” sesi duymazsınız. Sadece boş boş beklersiniz (Application Freeze).
İşte bu yüzden DNS sorunları, Timeout hatalarının en sinsi nedenidir. TCP bağlantısı hiç başlamadığı için, TCP Timeout süresi de işlemez. Uygulama kendi içindeki “DNS Timeout” süresince kilitlenir.
💡 İpucu: Eğer
curl,sshveyamysqlkomutunu yazar yazmaz imleç donuyorsa ve hiçbir çıktı vermiyorsa, sorun %99 DNS’tir.
/etc/resolv.confdosyanızı kontrol edin.dig +short <hostname>komutu anında cevap veriyor mu bakın.
🔍 Teknik Detay: strace Çıktısında Neden Socket Var?
sequenceDiagram
participant App as Uygulama (curl/browser)
participant Glibc as glibc (getaddrinfo)
participant Nscd as nscd / systemd-resolved
participant DNS as DNS Sunucusu (8.8.8.8)
App->>Glibc: "Google'ın numarası kaç?"
Glibc->>Nscd: "Önbelleğinde var mı? (UNIX Socket)"
alt Önbellekte Var
Nscd-->>Glibc: "Evet: 142.250.72.14"
else Önbellekte Yok
Nscd->>DNS: "Google kaç? (UDP/53)"
DNS-->>Nscd: "142.250.72.14"
Nscd-->>Glibc: "Öğrendim: 142.250.72.14"
end
Glibc-->>App: "IP Adresi: 142.250.72.14"
Dikkatli bir göz şunu sorabilir: “Ben strace çıktımda UDP (SOCK_DGRAM) görmüyorum, onun yerine /var/run/nscd/socket diye bir şey görüyorum. Bu nedir?”
Modern Linux dağıtımları, DNS sorgularını hızlandırmak için nscd (Name Service Cache Daemon) veya systemd-resolved gibi yerel önbellek servisleri kullanır:
- Uygulamanız (örneğin
curl),getaddrinfo()fonksiyonunu çağırır. glibc, sistemdenscdçalışıyor mu diye bakar.Eğer çalışıyorsa, DNS sunucusuna gitmek yerine UNIX Domain Socket üzerinden yerel servisle konuşur.
🧐 Teknik Detay: UNIX Domain Socket Nedir?
Klasik soketler (Network Sockets) veri transferi için ağ kartını ve IP protokolünü kullanır. UNIX Domain Sockets (
AF_UNIX) ise aynı bilgisayar içindeki süreçlerin (processes) konuşması içindir. Veri ağ kartına hiç uğramaz, doğrudan Kernel belleği üzerinden ışık hızında kopyalanır. “Ağsız Ağ” gibi düşünebilirsiniz.straceçıktısında şunu görürsünüz:1 2
socket(AF_UNIX, SOCK_STREAM|SOCK_CLOEXEC|SOCK_NONBLOCK, 0) = 3 connect(3, {sa_family=AF_UNIX, sun_path="/var/run/nscd/socket"}, 110) = 0
Bu kafanızı karıştırmasın. Bu sadece “Rehbere bakma” işleminin, dışarıdaki postane yerine, binadaki sekretere sorulmasıdır. Eğer yerel servis cevabı bilmiyorsa, o sizin yerinize DNS’e (UDP/53) gidecektir.
☸️ Kubernetes Dünyasından Gerçek Bir Vaka: ndots:5 Sorunu
Kubernetes (K8s) kullanıyorsanız, DNS performansı kritik bir darboğaz olabilir. K8s varsayılan olarak /etc/resolv.conf içine ndots:5 ayarını ekler.
Bu şu demektir: Siz google.com (tek nokta) adresine gitmek istediğinizde, sistem bunu “Tam nitelikli alan adı değil” (FQDN) sanar ve sırasıyla şunları dener:
google.com.default.svc.cluster.local(DNS Kaydı Yok - NXDOMAIN)google.com.svc.cluster.local(NXDOMAIN)google.com.cluster.local(NXDOMAIN)- …
- En sonunda gerçek
google.com.adresini sorar.
Her basit istek için dışarıya 5 boş DNS sorgusu atarsınız. Çözüm? External domainlere bağlanırken sonuna nokta koyun: google.com. (Absolute Domain Name). Bu, kernel’a “Arama yapma, adres tam olarak bu” der.
🎧 5. Localhost Tuzağı: 0.0.0.0 vs 127.0.0.1
Bir geliştiricinin en sık kurduğu cümle: “Ama benim makinemde (localhost’ta) çalışıyor, sunucuya atınca çalışmıyor!”
Uygulamanızı başlatırken (Bind aşamasında) genelde bir “Listening Address” (Dinleme Adresi) verirsiniz.
127.0.0.1(Localhost): “Sadece bu bilgisayarın içinden gelen istekleri kabul et.” (Kişisel, içe kapanık)0.0.0.0(Any / Wildcard): “Bu bilgisayardaki TÜM ağ kartlarından (Wifi, Ethernet, VPN vb.) gelen istekleri kabul et.” (Sosyal, dışa açık)
Eğer server.js veya app.py dosyanızda varsayılan olarak host='localhost' veya bind='127.0.0.1' yazıyorsa, dış dünyadan (hatta aynı ağdaki diğer sunucudan) kimse size ulaşamaz.
Kernel, dışarıdan gelen pakete bakar:
- Paket
192.168.1.5:8080adresine geldi. - Kernel dinleyenler listesine bakar.
- Uygulama
127.0.0.1:8080dinliyor. - Kernel: “IP’ler eşleşmiyor. Uygulama sadece içeriyi dinliyor. Gelen paket dışarıdan.”
- Sonuç: Connection Refused. (Sanki port kapalıymış gibi!)
Bunu ss (Socket Statistics) aracıyla şıp diye anlarsınız. netstat eskidi, artık ss kullanın.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
# -l: listening ports
# -n: show numbers (don't resolve service names)
# -t: tcp only
# -p: show process name (sudo gerekir)
sudo ss -lntp
# Çıktı Örneği 1: Utangaç Servis (Yanlış)
State Recv-Q Send-Q Local Address:Port Peer Address:Port Process
LISTEN 0 128 127.0.0.1:3000 0.0.0.0:* users:(("node",pid=123,fd=18))
# Sadece 127.0.0.1 dinliyor. Dışarıdan erişilemez!
# Çıktı Örneği 2: Sosyal Servis (Doğru)
State Recv-Q Send-Q 0.0.0.0:80 0.0.0.0:* users:(("nginx",pid=456,fd=6))
# 0.0.0.0 dinliyor. Her yerden erişilebilir.
Production ortamında dışarıya hizmet verecek bir servis, mutlaka 0.0.0.0 (veya sunucunun spesifik dış IP’sini) dinlemelidir.
🐳 Docker’ın “Sihirli” IP Adresleri
Docker konteynerlarında localhost (127.0.0.1) kavramı daha da kafa karıştırıcıdır.
- Konteyner İçinde:
127.0.0.1o konteynerin kendisidir. Host makine değildir. - Eğer konteyner içindeki uygulamanızdan Host üzerindeki MySQL’e bağlanmak için
localhostyazarsanız, uygulama konteynerin kendi içinde MySQL arar ve bulamaz. - Çözüm: Docker’ın özel DNS ismi
host.docker.internal(Mac/Windows) veya Linux’ta--network="host"kullanmak ya da Host IP’sini (172.17.0.1gibi) vermektir.
🍽️ 6. Restoran Analojisi: Backlog Queue
Sitenize “Black Friday” yoğunluğu başladı. Load Balancer’a bakıyorsunuz, sunucu ayakta ama kullanıcılar sürekli “Timeout” alıyor. Sunucuya girip curl 127.0.0.1 yapıyorsunuz, o bile yavaş cevap veriyor.
Burada socket, bind, listen üçlüsündeki listen(fd, backlog) parametresine dönelim. Kernel’ın bu trafiği nasıl yönettiğini anlamak için “Popüler Restoran” analojisini kullanalım.
flowchart TD
Client["Müşteri (Client)"] -- "SYN Paketi" --> SYNQ["SYN Queue<br/>(Kapı Önü Kuyruğu)"]
SYNQ -- "Yer Var mı?" --> Check{"Kontrol: Dolu mu?"}
Check -- "Evet (Taşmış)" --> Drop["DROP (Sessiz Red)"]
Check -- "Hayır (Yer Var)" --> SYNACK["SYN-ACK Gönder"]
SYNACK -- "ACK Geldi" --> AcceptQ["Accept Queue<br/>(İçeride Masa Bekleme)"]
AcceptQ -- "Sıra Geldi" --> App["Uygulama (accept syscall)"]
style Drop stroke:#f66,stroke-width:2px
style AcceptQ stroke:#fc0,stroke-width:2px
style SYNQ stroke:#6cf,stroke-width:2px
Bir TCP bağlantısı (müşteri) için iki bekleme alanı vardır:
- Dışarıdaki Kuyruk (SYN Queue): Müşteri restoranın kapısına geldi, “Alo” (SYN) dedi. Kapıdaki görevli ismini listeye yazdı ama henüz içeri almadı.
- İçerideki Bekleme Alanı (Accept Queue): Müşteri içeri alındı (ACK yollandı, bağlantı kuruldu - ESTABLISHED). Ancak henüz masaya oturtulmadı. Hostesin (yani Uygulamanızın, örn: Nginx/Tomcat) gelip “Buyrun masanız hazır” demesini (
accept()çağrısını yapmasını) bekliyor.
Kernel parametresi olan backlog, işte bu İçerideki Bekleme Alanı’nın kapasitesidir.
Eğer uygulamanız (Hostes) çok yavaş hareket ediyorsa (Tek thread çalışıyor, CPU %100, Disk I/O bekliyor vb.), içerisi dolar. İçerideki bekleme alanı (Backlog) dolduğunda, Hostes kapıdaki güvenliğe (Kernel) “Kimseyi alma!” talimatı verir.
Ve Kernel acımasızlaşır: Dışarıdaki kuyrukta (SYN Queue) bekleyen veya yeni gelen müşterilerin paketlerini sessizce çöpe atar (DROP).
Müşteri (Client) ise bunu “Restoran (Sunucu) cevap vermiyor, acaba kapalı mı?” (Timeout) sanır. Halbuki restoran açık, ışıklar yanıyor, sadece bekleme alanı taştı ve Kernel yeni müşterileri “görmezden geliyor”. Bu, bir Connection Refused değildir, bu bir sessiz reddediştir.
🚨 Kernel Bağırıyor:
dmesg“Sunucuda hiçbir hata yok” demeden önce
dmesgveya/var/log/kern.logdosyasına bakın. Kernel orada çığlık atıyor olabilir:
1 2 [1234.56] TCP: request_sock_TCP: Possible SYN flooding on port 80. Sending cookies. [1234.56] Check SNMP counters.Bu mesajı görüyorsanız,
backlogkuyruğunuz taşmış demektir.
Kontrol etmek için yine ss imdadımıza yetişir:
1
ss -lnt
Çıktıdaki Recv-Q ve Send-Q sütunlarına dikkat! Normalde Recv-Q (Bekleyenler) 0 olmalıdır.
1
2
3
State Recv-Q Send-Q Local Address:Port
LISTEN 0 128 0.0.0.0:80 (Normal Durum)
LISTEN 129 128 0.0.0.0:8080 (KRİTİK DURUM!)
İkinci satırda Recv-Q (129), Send-Q (Limit olan 128)’i geçmiş. Bu demektir ki kuyruk taşmış. Kernel artık bu porta gelen paketleri çöpe atıyor.
Çözüm:
- Uygulamanızın
backlogparametresini artırın (Nginx’telisten 80 backlog=4096;). - Kernel’ın global limitini artırın (
sysctl -w net.core.somaxconn=4096). - Veya uygulamanızın neden bu kadar yavaş
accept()ettiğini (CPU bottleneck, Blocking I/O) araştırın.
🛠️ Network Debug Karar Matrisi
Kafalar karışmasın diye, işte size duvara asmalık bir yol haritası:
| Belirti (Semptom) | Olası Neden | İlk Bakılacak Araç/Komut |
|---|---|---|
| Connection Refused | Servis çalışmıyor, Port Yanlış, veya Localhost Dinliyor | sudo ss -lntp (Sunucuda servis var mı?) |
| Connection Timed Out | Firewall (DROP kuralı), Routing Hatası, IP Yanlış | tcpdump, traceroute, telnet |
| Ping var, Telnet yok | Firewall (ICMP açık, TCP kapalı) | nc -zv <ip> <port> |
| Anında Hata (No Route to Host) | Routing tablosu bozuk / IP aynı subnet’te değil | ip route, ip addr |
| Bekliyor… Bekliyor… | DNS Çözümleme yavaşlığı veya UDP engeli | dig, nslookup, strace (DNS sorgusunu gör) |
| Rastgele/Aralıklı Timeout’lar | Backlog Queue taşıyor, SYN Flood, Paket Kaybı | ss -lnt (Recv-Q kontrolü), dmesg |
| Çok Yavaş Bağlantı (SSH vb.) | Reverse DNS Timeout | Sunucu config (UseDNS no) |
- Eğer SSH bağlantısı çok yavaş kuruluyorsa, sunucu sizin IP’nizi Reverse DNS (PTR) ile çözmeye çalışıyordur. SSH sunucu konfigürasyonunda
UseDNS noyaparak bu çileyi bitirebilirsiniz.
🧐 Teknik Detay: Reverse DNS (PTR) Nedir?
Normal DNS “isimden IP bulma” (google.com -> 1.2.3.4) işlemidir. Reverse DNS ise “IP’den isim bulma” (1.2.3.4 -> google.com) işlemidir. Sunucular bazen güvenlik için “Bana bağlanan bu IP gerçekten kim?” diye ters sorgu yapar. Eğer IP’nizin PTR kaydı yoksa, sunucu cevabın timeout’a düşmesini bekler. O meşhur “10 saniyelik SSH beklemesi” bundandır.
