Bölüm 67: Güvenilir Aktarım
Ağlar'a “Sen”i Katmak kitabının bir bölümü: adres çubuğuna yazdığın tek bir isteğin peşinden bakırla fiberin fiziğine inip karşı taraftan geri dönen uzun bir teknik yolculuk.
Kısım 9: Karşı Tarafta ve Dönüşte (Bölüm 65-70)
Tüm bölümler
- Giriş
- Adres Çubuğundan İsteğe
- Paket Anahtarlamalı Yol
- İsimden Adrese
- DNS Kayıtları
- Önbellek, Tazelik ve Güven
- Port Numarası ve Soket
- Bağlantısız Taşıma: UDP
- Üç Adımlı El Sıkışma
- Simetrik Şifreleme
- Açık Anahtarlı Kriptografi
- TLS El Sıkışması
- Sertifika Zinciri
- HTTP Mesajının Anatomisi
- Durum, Çerez ve Oturum
- HTTP/2'den HTTP/3'e
- İçerik Dağıtım Ağları
- Diğer Uygulama Protokolleri
- Katmanlı Mimari
- Hizmet ve Protokol
- Encapsulation ve Başlıklar
- Paket Yakalama ve Çözümleme
- Ağ Interface Kartı
- Fiziksel Adresleme
- Hata Sezme ve CRC
- Adres Çözümleme: ARP
- Ortam Erişimi: CSMA/CD
- Switch'ler
- Ağ Topolojileri
- Sanal Yerel Ağlar
- Kablosuz Erişim: 802.11
- Radyo Kanalı ve Kapasite
- Kablosuz Ağ Güvenliği
- Hücresel Ağlar
- Spektrum ve Kuşaklar
- Hareketlilik ve Aktarma
- Adres Kiralama: DHCP
- Adresin Anatomisi
- Adres Tükenmesi ve IPv6
- Router Mimarisi
- Kuyruk Yönetimi
- Cihaz Yapılandırma
- Routing Protokolleri
- Kontrol Düzlemi ve SDN
- Ağ İzleme ve Yönetimi
- Alanlar Arası Routing
- Adres Çevirisi
- Ara Kutular
- Tünelleme ve IPsec
- Firewall'lar
- Ağ Saldırıları
- Operatör ve Metro Ağları
- Bitten Sinyale
- Sembol Hızı ve Kanal Kapasitesi
- Multiplexing Teknikleri
- İletim Ortamları
- Bakır Kablo Sonlandırma
- Fiber Sonlandırma
- Fiber Optik İletim
- Bakır Erişim Ağı: DSL
- DSL'de Hız ve Mesafe
- Pasif Optik Ağlar
- Kablo Erişim Ağları
- Kablo Modem ve CMTS
- Denizaltı Kabloları
- Veri Merkezi Ağları
- Encapsulation Çözme
- Güvenilir Aktarım
- Tıkanıklık Denetimi
- Trafik Çözümleme
- Gecikme Bütçesi
- Soket Programlama
- Eşten Eşe ve Protokol Tasarımı
- Uyarlanır Video Akışı
- Gerçek Zamanlı Ses ve Görüntü
- Kablosuz Sensör Ağlarının Mimarisi
- Sensör Ağlarında Ortam Erişimi
- Sensör Ağlarında Yönlendirme ve IP
- Zaman Eşitleme ve Konum Bulma
- Sensör Ağı Uygulamaları
- Mobil Kablosuz Sensör Ağları
- Son Söz
Sunucu yanıtı hazırladı ve geri gelen şey artık küçük bir istek değil. Karşıya geçecek gövde 292.000 bayt, yani 1.460 baytlık iki yüz parça. Değişen tek şey boyut gibi duruyor.
Değişen şey boyutun getirdiği sorun. Parçalardan biri yolda kayboluyor. Gönderen bunu kimden öğrenecek?
Her oktetin bir numarası var
TCP’nin verdiği söz üç parçalı: güvenilir, sırası bozulmamış ve mesaj sınırı olmayan bir bayt stream’i. Bu sözün altındaki mekanizma tek bir fikirden çıkıyor. Bağlantı üzerinden giden her oktetin bir sıra numarası var. Standardın en temel tasarım kararı bu ve bölümün geri kalanındaki her mekanizma ondan türüyor.
Numara segmentleri saymıyor, baytları sayıyor. Başlıktaki alan, o segmentin ilk baytının stream içindeki numarasını taşır. Yanıtın ilk segmenti 7.000 ile çıkıyorsa ikincisi 8.460, üçüncüsü 9.920, dördüncüsü 11.380 olur. Aradaki fark hep 1.460.
O 1.460 sayısı da havadan gelmiyor. 20. bölümde MTU’dan iki başlığı düşünce kalan tam boy segment bu; encapsulation merdiveninin taşımaya bıraktığı yer.
Onay numarasını okurken sık yapılan bir hata var: onu alınan son bayt sanmak. Gösterdiği şey bir sonraki beklenen bayt. Üstelik birikimli çalışıyor; 9.920 onayı, 9.920 hariç ondan öncekilerin tamamının alındığını söylüyor. Bu kural kopya saptamayı bedavaya getirir, çünkü yeniden gönderim varken aynı baytı ikinci kez alan taraf onu numarasından tanır.
Telefonda birine uzun bir adres yazdırdığını düşün. Karşı taraf her satırın sonunda “kapı numarasına kadar aldım, devamı?” der. Söylediği şey aldığının sonu değil, beklediğinin başıdır; o cümle onay numarasıdır ve “devamı” dediği yer beklenen bayttır. Aynı cümleyi ikinci kez duyman da aradan bir şey düştüğünün işaretidir. Tek fark şu: karşındaki insan söylediğini anlar, TCP yalnızca sayar.
Sıra uzayı 0 ile 2^32 - 1 arasındadır ve bütün aritmetik 2^32 modunda yürür. Havuzda 4.294.967.296 numara var. Sonuncudan sonra sayaç başa döner. Başlıktaki alan genişlikleri de bu muhasebeyi doğruluyor:
sıra numarası 32 bit (4 bayt) segmentin ilk baytının numarası
onay numarası 32 bit (4 bayt) beklenen bir sonraki bayt
pencere 16 bit (2 bayt) alıcının o an kabul edebileceği baytHavuzun sonlu olması gerçek bir sınır koyar. Sayaç başa dönmeden önce eski bir kopyanın ağda ölmüş olması gerekir. Hızı bilince süre çıkar. 1 Gbit/s’lik bir stream’de saniyede 125 MB gider; havuz 34,4 saniyede başa döner. Numaranın bir sonraki tura kalan ömrü, segmentin ağda yaşayabileceği en uzun süreden kısa olamaz. 10 Gbit/s’te ise havuz 3,4 saniyede dönüyor; bu, standardın bir segmente tanıdığı en uzun ömürden (MSL, iki dakika) çok kısa. Hızlı hatlarda aynı numarayı taşıyan iki kopyayı ayırt etmeyi bu yüzden zaman damgası seçeneği üstleniyor (PAWS, RFC 7323).
8. bölümde başlangıç sıra numarası her bağlantıda yeniden üretiliyordu; o numaranın neyi saydığı burada çalışıyor.
Dur ve bekle hattı boş bırakıyor
Güvenilirlik tek başına hattı verimli kullanmaya yetmiyor. Dur ve bekle protokolü doğru çalışır: gönderen bir segment yollar, onayı bekler, sonra bir tane daha yollar. Boruda aynı anda tek segment olur.
Bedeli hesaplanabilir. 1.460 baytlık bir segment 100 Mbit/s’lik bir hatta 116,8 µs’de çıkar. Gidiş-dönüş 30 ms ise gönderen her segment için o kadar bekler. Hat zamanının yalnızca %0,39’unda çalışır.
Çözüm boru hattıdır: gönderen onay beklemeden birden çok segmenti havada tutar. Üç paketlik bir boru hat kullanımını üç katına çıkarır. Bedeli de var. Sıra numarası aralığı genişlemek ve iki uçta da tampon açılmak zorundadır.
Havada tutulabilen o aralığın adı sliding window’dur. Gönderenin sıra uzayı dört bölgeye ayrılır: onaylanmış baytlar, gönderilmiş ama onaylanmamış baytlar, pencerede olup henüz gönderilmemiş baytlar ve pencere dışı. Sol kenarı tutan değişkenin adı SND.UNA’dır; alıcı tarafta alma penceresinin sol kenarı RCV.NXT’tir.
Gönderen uçuştaki segmentlerin kopyalarını bir yeniden gönderim kuyruğunda saklar. Onay geldiği anda kopya atılır ve sol kenar sağa kayar. Pencereyi kaydıran şey onaydır. Atma kuralı da kesindir: bir segmentin sıra numarası ile uzunluğunun toplamı gelen onayı geçmiyorsa o segment kuyruktan düşer.
Sunucu 203.0.113.10, istemci 198.51.100.7 olsun. SND.UNA 7.000’de duruyor ve alıcı 11.680 baytlık pencere ilan etti. Gönderen 18.680’e kadar yeni bayt yollayabilir, yani sekiz tam boy segment. 8.460 onayı gelince sol kenar oraya kayar. Sağ uç 20.140’a çıkar; ilan değişmese bile pencere sağa yürüdü.
Pencerenin iki kenarının onay geldikçe nasıl yürüdüğünü izleyelim:
- Akış baytlara numaralanıyor. Yanıt gövdesi 1.460 baytlık segmentlere bölünüyor ve her segmentin ilk baytı bir sıra numarası taşıyor. Numara segmenti değil, baytı sayıyor; aradaki fark her seferinde segmentin yükü kadar.
- Pencere üç segmenti kapsıyor. Gönderen akışın tamamını bir anda yollayamıyor. Alıcının ilan ettiği pencere, o an gönderilmesine izin verilen aralığı işaretliyor; buradaki örnekte üç segment genişliğinde.
- Uzay dört bölgeye ayrılıyor. Solda onayı gelmiş baytlar duruyor. Pencerenin içinde gönderilmiş ama onaylanmamış segmentlerle henüz gönderilmemiş bir segment var; sağda ise izin sınırının ötesi kalıyor.
- Onay sol kenarı ileri itiyor. 9.920 onayı geliyor ve ondan önceki bütün baytları tek seferde kapatıyor. Uçuştaki ilk segment onaylanmış bölgeye düşüyor, sol kenar da onun bittiği yere taşınıyor.
- Pencere sağa kayıyor. Sol kenar ilerleyince sağ kenar da aynı miktarda ilerliyor ve daha önce sınırın dışında kalan segment gönderilebilir hâle geliyor. Pencereyi kaydıran şey onayın kendisi.
66. bölümde gelen frame’in sağlamasına bakılıyor ve veri soket kuyruğuna bırakılıyordu; buradaki eksik bayt o denetimin yakaladığı bir bozulma değildir.
Sayaç ne kadar bekleyecek?
Kaybı saptamanın ilk yolu gönderendeki bir sayaçtır; süresinin adı yeniden gönderim zaman aşımıdır. Süre gidiş-dönüşten uzun olmalı. Ama gidiş-dönüş sabit değildir. Kısa seçersen ortada kayıp yokken gereksiz kopyalar üretirsin; uzun seçersen gerçek kayba geç tepki verirsin.
Ölçümün kendisi basit. Örnek RTT, bir segmentin gönderilmesiyle onayının gelmesi arasında geçen süredir. Kestirim tek ölçümden çıkmaz. Üstel ağırlıklı hareketli ortalama kullanılır ve eski örneklerin etkisi hızla söner. Ağırlıklar sabittir: kestirim için 1/8, sapma için 1/4.
Üstüne bir güvenlik payı biner. RFC 6298 payı sapmanın dört katı olarak tanımlar. İlk ölçümün kendine ait bir kuralı var. Kestirim doğrudan ölçümün kendisi olur, sapma da onun yarısı. Hiç ölçüm yokken gönderen 1 saniyelik başlangıç değeriyle çalışır.
İlk ölçümün 32 ms geldiğini varsay: kestirim 32 ms, sapma 16 ms, pay da 64 ms olur. Ham zaman aşımı 96 ms çıkar. Ama taban 1 saniyedir ve her hesaba uygulanır: değer onun altına düşerse yukarı yuvarlanır. Linux bu tabanı 200 ms’ye indiriyor; standart temkinli, işletim sistemleri daha cesur. İkinci ölçüm 30 ms gelsin. Kestirim 31,75 ms’ye, sapma 12,5 ms’ye iner, ham değer 81,75 ms olur ve taban onu yine 1 saniyeye çeker. Zaman aşımına üst sınır konabilir, ama o sınır 60 saniyenin altına inemez.
Bir tuzak var. Karn kuralı yeniden gönderilmiş bir segmentten örnek almayı yasaklar; gelen onayın hangi kopyaya ait olduğu belirsizdir. Üstel geri çekilme de buradan gelir: sayaç her dolduğunda değer ikiye katlanır ve ardışık denemeler 1, 2, 4, 8, 16, 32 saniye olur.
Gönderen her segmente ayrı sayaç kurmaz. Tek sayaç en eski onaylanmamış segment için işler ve dolunca yalnızca o segment yeniden gider. Bekleyen bütün veri onaylandığında zamanlayıcı kapanır, yeni veri çıkınca yeniden kurulur.
40. bölümde kuyruk gecikmesi trafikle birlikte oynuyordu; zaman aşımının sabit bir sayı olamamasının sebebi o oynama.
Onay yinelenince beklemenin anlamı kalmıyor
Sayaç, kaybı yakalamanın tek yolu olarak kalmıyor. İkinci yol alıcının kendi davranışından çıkar.
Sırayla gelen ilk segment hemen onaylanmaz. Alıcı bir sonrakini bekler ve ikisini tek onayla kapatmayı dener; bu beklemenin adı gecikmeli onaydır. Sırada bir boşluk açılınca o bekleme hemen düşer.
| alıcıya gelen | alıcının yaptığı |
|---|---|
| sırayla gelen ilk segment | onayı en fazla 500 ms geciktirir |
| sırayla gelen ikinci segment | ikisini tek onayla kapatır |
| sırası atlanmış segment | beklediği baytın onayını hemen yineler |
| boşluğu dolduran segment | boşluğun ötesini de kapatan onayı yollar |
Boşluk kapanana kadar alıcı hep aynı onayı yollar; gönderen o numarayı üst üste görür. Üç yinelenen onay geldiğinde sayacı beklemez. Hızlı yeniden gönderim en küçük sıra numaralı onaylanmamış segmenti hemen yola çıkarır. Eşiğin üç seçilmesinin sebebi ağın segmentleri yeniden sıralamasıdır; yinelenen onay her zaman kayıp anlamına gelmez ve daha düşük bir eşik yöntemi boşuna tetiklerdi. Bugünün Linux’u kaybı artık onay saymakla değil, zamana bakarak saptıyor (RACK, RFC 8985): kendisinden sonra gönderilen bir segmentin onayı gelmiş ve üstünden yeterince zaman geçmişse, önceki segment kayıp sayılıyor.
Kazanç ölçülebilir. 30 ms’lik bir yolda 1 saniyelik zaman aşımını beklemek yerine yaklaşık 33 kat erken tepki verirsin. Ayrı bir olumsuz onay mesajına da gerek kalmaz; yinelenen onay gönderende aynı işi görür.
Her eksik onay yeniden gönderim doğurmaz. 8.460 onayının kaybolduğunu düşün. 9.920 onayı sayaç dolmadan gelirse boşluk kapanır ve hiçbir segment tekrar gitmez.
Onay, karşıya akan bir veri segmentinin başlığına da binebilir; bu birleştirmenin adı piggybacking. Kopya bir segment vardığında alıcı onu sessizce atmaz. Kendi onayının kaybolduğunu varsayıp kopyayı da onaylar.
Gecikmeli onayın bedeli de var. Her segmenti anında onaylamak ağa veri taşımayan segmentler ekler. Beklemek o yükü kısar ama gönderenin kestirimini bozar; ölçülen süreye alıcının bekleyişi de karışır.
Derinleşmeİki iyi niyetli optimizasyon çarpışırsa ne olur?
Alıcı tarafındaki gecikmeli onayın gönderen tarafında bir kardeşi var: John Nagle’ın 1984’te önerdiği Nagle algoritması. İkisi de aynı derdi çözüyor: küçük segmentlerin ağa gereksiz yük bindirmesi. 66. bölümde hesapladığımız uç vakayı hatırla: tek bir baytlık veri telde 84 bayt yer kaplıyor, yani zarfın payı %98,8.
Nagle’ın kuralı: onaylanmamış veri varken küçük bir segment gönderme, biriktir. Elde bir tam segment birikirse ya da bekleyen bütün onaylar gelirse gönder. Uzak bir makinede metin düzenlerken her tuş vuruşunun ayrı bir pakete binmesini böyle engelliyor.
Şimdi ikisini yan yana koy. Gönderen küçük bir parça yolladı ve Nagle yüzünden ikinciyi göndermek için onay bekliyor. Alıcı ise gecikmeli onay yüzünden, ikinci segmenti bekleyerek onayı geciktiriyor. İki taraf birbirini bekliyor.
Kilidi kıran şey alıcının zamanlayıcısı: gecikmeli onay en fazla beklediği kadar bekleyip onayı yolluyor. Linux’ta bu genellikle 40 ms. Yani hiçbir kayıp, hiçbir tıkanıklık olmadan, tamamen boş bir hatta, iki iyi niyetli optimizasyon yüzünden 40 milisaniyelik bir duraklama doğuyor. Küçük istek-cevap çiftleriyle çalışan bir uygulamada bu, saniyede 25 işlem demek.
Çare uygulamanın elinde: tek bir soket ayarıyla (TCP_NODELAY) Nagle kapatılabiliyor ve gecikmeye duyarlı hemen her kütüphane bunu varsayılan olarak yapıyor. Doğru hamle de bu, çünkü bugün küçük segment yükü 1980’lerdeki kadar önemli değil; gecikme ise daha önemli.
Birikimli onay bir şeyi söyleyemiyor
Yeniden gönderimin kapsamı ayrı bir karardır ve iki uçtaki klasik yaklaşımın adları var. N geri git’te bir zaman aşımı penceredeki bütün üst numaralı segmentleri de geri yollar; alıcı sırası bozuk geleni hiç tamponlamaz, bu yüzden alıcı basit ama hat israf edilir. Seçici tekrarda yalnızca eksik olan gider ve alıcı sırasızları tamponunda tutar; hat verimli ama alıcı karmaşıklaşır.
TCP ikisinin arasında bir yerde duruyor ve sıkıntısı birikimli onaydan geliyor. Onay numarası yalnızca “şu bayta kadar geldi” diyebiliyor; ötesinde neyin geldiğini söyleyemiyor. Somut bir örnek: gönderen 7.000, 8.460, 9.920, 11.380 ve 12.840 numaralı beş segment yolladı ve yalnızca 8.460 kayboldu. Alıcının elinde dördü var ama söyleyebildiği tek şey “8.460 bekliyorum”. Gönderen dört sağlam segmenti de yeniden yollamak ile yalnızca birini yollayıp riske girmek arasında kalıyor.
Çözüm bir seçenek. Seçici onay (SACK), onay numarasının yanına “şu aralıklar da elimde” listesini ekliyor. 8. bölümdeki el sıkışmada gördüğün SACK izinli seçeneği tanıdık; bu, o iznin karşılığı.
onay = 8.460 SACK: 9.920-14.300İki satır birlikte okununca resim tamamlanıyor: 8.460 eksik, ondan sonrası tamam. Gönderen tek bir segment yolluyor ve boşluk kapanıyor. Seçenek alanı 40 baytla sınırlı olduğu için tek seferde en fazla dört blok bildirilebiliyor; zaman damgası seçeneği de açıksa bu sayı üçe iniyor.
Kazanç, kayıpların tek tek değil kümeler hâlinde olduğu durumlarda büyüyor. Bir tıkanıklık anında bir pencerede birkaç segment birden düşer; SACK olmadan gönderen bunları ancak arka arkaya zaman aşımlarıyla, tur tur keşfeder. SACK ile hepsini tek turda görür.
Direksiyon alıcıda
Kayıp toparlandı. Geriye tek bir soru kalıyor: alıcı yetişemezse ne olacak?
Flow control bu soruyu alıcının eline veriyor. Gönderen uçuştaki veri miktarını alıcının ilan ettiği pencereyle sınırlar. İlan edilen sayı alma penceresidir: alıcının tamponunda o an kalan boş yer.
Tamponun dolma sebebi ağ kartı değil. 66. bölümde gördüğün gibi TCP gelen yükü soket tamponuna bırakır, uygulama da oradan okur. Uygulama okumakta geri kalırsa boş yer erir ve ilan edilen sayı küçülür. Pencereyi olduğundan büyük göstermek işi düzeltmez. Kabul edilemeyen veri atılır ve gereksiz yeniden gönderim doğar.
Onay ile izin de ayrı şeylerdir. Alıcı bir baytı onaylayıp yeni gönderime izin vermeyebilir. TCP ikisini tek başlıkta taşır; onay numarası ile pencere yan yana durur.
Pencere alanı 16 bit olduğu için ölçeklemesiz en büyük ilan 65.535 bayttır. 100 ms gidiş-dönüşte bu, yaklaşık 5,24 Mbit/s’lik bir tavan demektir. Pencere ölçekleme uzantısı (RFC 7323) alandaki değeri en fazla 14 bit sola kaydırarak o tavanı 1 GiB’a kaldırır. Tavanın ne kadar alçakta kaldığını bir hesap gösterir: 10 Gbit/s’i 100 ms gidiş-dönüşte tutturmak için 1.500 baytlık 83.333 paketin, yani yaklaşık 125 MB verinin aynı anda havada olması gerekir.
Pencere sıfırlandığında gönderen büsbütün durmak zorunda kalır. Pencereyi yeniden açan segment kaybolursa iki taraf birbirini bekler. Kilidi ısrar zamanlayıcısı kırar: dolduğunda gönderen, öncekinin kopyası olsa bile en az bir oktetlik bir yoklama yollar. Ardışık yoklamalar arasındaki süre üstel olarak açılır.
Flow control’ün koruduğu şey yalnızca alıcının kapasitesi. Ağın ortasındaki kuyrukları koruyan mekanizmayı bir sonraki durakta açacağım; pencere boyunu ikisi birlikte belirler.
Bunları kendi makinende görebilirsin. Linux’ta ss -ti çıktısı açık bir bağlantı için rtt ve rto alanlarını basar; rtt değerinin yanındaki ikinci sayı sapmadır. netstat -s ise TCP sayaçlarını döker; yeniden gönderilen segment sayısı orada durur.
Böylece omurga sorularından ikincisini kapatabiliriz.
Omurga sorusu 2: Kaybolabilen bir yolun üstünde kaybolmayan bir aktarım nasıl kuruluyor?
Numaralayarak. Her oktet numaralanır, her onay beklenen numarayı söyler, bir sayaç ile üç yinelenen onay kaybı iki ayrı yoldan yakalar, pencere de gönderenin ne kadar ileri gidebileceğini sınırlar. Yol hiçbir zaman güvenilir olmadı; güvenilirlik iki uçtaki muhasebeden çıktı.
Özet
- Sıra numarası: alan segmentleri değil baytları sayar ve segmentin ilk baytının stream içindeki yerini taşır.
- Birikimli onay: onay numarası beklenen bir sonraki baytı gösterir ve kendinden öncekilerin hepsini tek seferde kapatır.
- Zaman aşımı: süre ölçülen tek bir gidiş-dönüşten değil, düzleştirilmiş kestirime eklenen sapma payından çıkar.
- Hızlı yeniden gönderim: üç yinelenen onay, sayacın dolmasını beklemeden eksik segmenti yola çıkarır; Linux aynı işi bugün zamana bakarak yapıyor.
- Seçici onay birikimli onayın körlüğünü kapatır:
SACKseçeneği “şu aralıklar da elimde” diyerek gönderenin yalnızca gerçekten eksik olanı yollamasını sağlar.- Flow control: alıcının ilan ettiği pencere, gönderenin uçuşta tutabileceği veri miktarına tavan koyar.
Kayıp artık kendi kendine toparlanıyor ve alıcı gönderene ne kadar yer kaldığını söylüyor. Ama yolun ortasındaki kuyruklar kimseye bir şey ilan etmiyor. Gönderen ne kadar hızlı göndereceğine neye bakarak karar veriyor?
68. bölüme devam et: Tıkanıklık Denetimi