---
title: 'Ağ İstemcisi'
source: 'https://academia.sh/tr/kurslar/ios-veri-ve-ag/ag-istemcisi'
course: 'Veri, Ağ ve Eşzamanlılık'
language: tr
updated: '2026-10-21T05:03:09+00:00'
license: 'CC BY-SA 4.0'
objectives:
    - 'Oturumun, yapılandırmanın ve görevin ağ isteğindeki ayrı rollerini ayırır'
    - 'Veri, yükleme ve indirme görev türlerinin hangi işe uyduğunu söyler'
    - 'Bir işlemin yinelenirse ya da sırası değişirse sonucunun değişip değişmediğini gerçekten ölçer'
    - 'Etkisizliğin yinelemeyi çözdüğünü ama sırayı çözmediğini ölçülmüş bir işlemle gösterir'
---

# Ağ İstemcisi

Oturum yapılandırması ve görev türlerinin anlatımı; ağ katmanının arkasındaki altı işlemin yinelenirse ya da sırası değişirse sonucunun değişip değişmediğinin dört kategoride ölçümü.

Önceki kursta (M30/K04) bir ekranın gösterdiği değeri modelin değeriyle karşılaştırdık ve ekranın bayat kalıp kalmadığına baktık. Bu kurs ekranın arkasına iner: değerin geldiği, saklandığı ve işlendiği katmana. Bu katmanda soru değişir. Ekran değeri gösterir ya da göstermez. Ağ katmanındaki bir işlem ise başka bir şeyle sınanır: aynı işlem iki kez uygulanırsa, ya da iki işlemin sırası değişirse sonuç farklı çıkar mı?

Bu sorunun ağda sık sorulması boşuna değil. İstek yolda kaybolabilir, yanıt geç gelebilir, kullanıcı aynı düğmeye iki kez basabilir, iki yanıt ters sırada ulaşabilir. Hiçbiri hata vermez. Yalnız durum sessizce başka bir değere döner. Bu derste önce ağ istemcisinin yapı taşlarını tanıyacağız: oturum, yapılandırma ve görev. Ardından altı işlemi tek bir çekirdekle ölçeceğiz.

Ölçü ekseni **duyarlılıktır**. Bir işlemi bir kez ve iki kez uygularız; sonuçlar farklıysa işlem **yinelemeye duyarlıdır**. İki işlemi iki ayrı sırayla uygularız; sonuçlar farklıysa işlem **sıraya duyarlıdır**. İkisi birden varsa **çift duyarlı**, hiçbiri yoksa **sağlam** denir. Çıktıda dört ad ASCII yazılır: `saglam`, `yinelemeye-duyarli`, `siraya-duyarli`, `cift-duyarli`.

```swift
// Replay.swift — bir islemin yinelemeye ve siraya duyarliligini siniflandirir
struct Trial<State: Equatable & CustomStringConvertible> {
    let label: String
    let once: State
    let twice: State
    let forward: State
    let backward: State

    var sensitivity: String {
        switch (once != twice, forward != backward) {
        case (false, false): return "saglam"
        case (true, false): return "yinelemeye-duyarli"
        case (false, true): return "siraya-duyarli"
        case (true, true): return "cift-duyarli"
        }
    }

    func report() -> String {
        print("\(label) | bir=\(once) iki=\(twice) ab=\(forward) ba=\(backward) -> \(sensitivity)")
        return sensitivity
    }
}

func replay<State>(_ label: String, from start: State, _ a: (State) -> State, then b: (State) -> State) -> Trial<State> {
    Trial(label: label, once: a(start), twice: a(a(start)), forward: b(a(start)), backward: a(b(start)))
}

func countSensitivities(_ results: [String]) {
    for name in ["saglam", "yinelemeye-duyarli", "siraya-duyarli", "cift-duyarli"] {
        print("\(name): \(results.filter { $0 == name }.count)")
    }
}
```

`Trial` dört durumu tutar: `once` işlemin bir kez, `twice` iki kez uygulanmasından çıkan durumdur. `forward` önce `a`, sonra `b`; `backward` önce `b`, sonra `a` uygulanmasından çıkar. `sensitivity` yalnız iki eşitsizliğe bakar. `replay` bu dört durumu bir başlangıç durumundan üretir. **Kural nedeni görmez, eşitsizliği görür:** iki durumun neden farklı olduğunu söylemez, yalnız farklı olduklarını söyler. Hangi durumun karşılaştırıldığını ders söyler.

## Oturum ve Yapılandırma

Bir ağ isteği üç ayrı nesne ister. **Yapılandırma** (configuration) oturumun nasıl davranacağını söyler: zaman aşımı, önbellek, ek başlıklar, ağ yoksa beklenip beklenmeyeceği. **Oturum** (session) bu yapılandırmayla çalışan istemcidir; aynı oturumdan çıkan istekler aynı kuralları paylaşır. **Görev** (task) tek bir aktarımdır: bir isteği alır, bir yanıt ya da hata döndürür.

`URLSessionConfiguration.default` kalıcı bir önbellek ve çerez deposu kullanır. `.ephemeral` bunların hiçbirini diske yazmaz; oturum kapanınca iz kalmaz. Üçüncü yapılandırma arka plan oturumudur ve bu konunun son dersinde işlenir.

```swift
import Foundation

let configuration = URLSessionConfiguration.ephemeral
configuration.timeoutIntervalForRequest = 15
configuration.waitsForConnectivity = true
configuration.httpAdditionalHeaders = ["Accept": "application/json"]
let session = URLSession(configuration: configuration)
```

Yapılandırma oturum kurulurken okunur. Oturum kurulduktan sonra yapılandırmayı değiştirmek oturumu etkilemez; değişiklik için yeni bir oturum kurulur. Bu yüzden uygulamalar çoğunlukla az sayıda oturum kurar ve onları her yerde paylaşır.

## Görev Türleri

Üç görev türü işi ayırır. **Veri görevi** (data task) yanıtı belleğe alır; küçük yanıtlar için uygundur. **Yükleme görevi** (upload task) isteğin gövdesini ayrı bir veri ya da dosya olarak gönderir. **İndirme görevi** (download task) yanıtı doğrudan bir dosyaya yazar; büyük gövdeler belleği şişirmesin diye kullanılır. Eşzamansız biçimde ilk ikisi `data(for:)` ve `upload(for:from:)` çağrılarıdır; ikisi de bir `(Data, URLResponse)` çifti döndürür.

```swift
// profil istegi — KAYNAK
import Foundation

func fetchProfile(session: URLSession) async throws -> Data {
    let request = URLRequest(url: URL(string: "https://api.example.com/profile")!)
    let (data, response) = try await session.data(for: request)
    guard let http = response as? HTTPURLResponse, http.statusCode == 200 else {
        throw URLError(.badServerResponse)
    }
    return data
}
```

Bu parça çalıştırılmaz; ağa çıkar. `data(for:)` bir **eşzamansız fonksiyondur** (asynchronous function): çağrı yanıt gelene kadar **duraklama noktasında** bekler ve akış sonra kaldığı yerden sürer. Yanıtın gerçek bir yanıt olup olmadığı ayrıca denetlenir; durum kodu `HTTPURLResponse` üzerinden okunur.

```swift
import Foundation

func send(order: Data, session: URLSession) async throws -> Int {
    var request = URLRequest(url: URL(string: "https://api.example.com/orders")!)
    request.httpMethod = "POST"
    let (_, response) = try await session.upload(for: request, from: order)
    return (response as? HTTPURLResponse)?.statusCode ?? 0
}
```

Yükleme görevinde gövde isteğin içine değil `from:` parametresine verilir. Bu parça da KAYNAK kalır. Yeni bir sipariş yaratan bir `POST` isteğinin yinelenince ne olduğu bu dersin altı işleminden biridir.

## Altı İşlem

Aşağıdaki **model**, her işlemin duruma ne yaptığını kurar; ağın ya da diskin kendisi değildir. Altı işlemin her biri ağ katmanının bildik bir işine karşılık gelir: bir önbelleğe yazma, etkisizlik anahtarlı bir gönderim, yeniden denenen bir sipariş, bir rozet sayısını artırma, bir tercihi yazma, bir günlüğe satır ekleme. Çekirdeğin `replay` işleviyle her biri bir kez, iki kez ve iki sırada uygulanır.

```swift
// main.swift — alti islem, dort duyarlilik
func cached(_ key: String) -> ([String]) -> [String] {
    { keys in Array(Set(keys + [key])).sorted() }
}

func submit(_ key: String) -> ([String]) -> [String] {
    { received in received.contains(key) ? received : received + [key] }
}

func placeOrder(_ count: Int) -> Int { count + 1 }

func badge(_ count: Int) -> Int { count + 1 }

func theme(_ value: String) -> (String) -> String {
    { _ in value }
}

func log(_ line: String) -> ([String]) -> [String] {
    { lines in lines + [line] }
}

let cacheWrite = replay("onbellege-yaz", from: [String](), cached("profil"), then: cached("akis")).report()
let keyedSend = replay("anahtarli-gonderim", from: [String](), submit("k-41"), then: submit("k-42")).report()
let retry = replay("yeniden-dene", from: 0, placeOrder, then: placeOrder).report()
let push = replay("rozet-artir", from: 3, badge, then: badge).report()
let preference = replay("tercih-yaz", from: "sistem", theme("koyu"), then: theme("acik")).report()
let journal = replay("gunluge-ekle", from: [String](), log("giris"), then: log("cikis")).report()

countSensitivities([cacheWrite, keyedSend, retry, push, preference, journal])
```

```
onbellege-yaz | bir=["profil"] iki=["profil"] ab=["akis", "profil"] ba=["akis", "profil"] -> saglam
anahtarli-gonderim | bir=["k-41"] iki=["k-41"] ab=["k-41", "k-42"] ba=["k-42", "k-41"] -> siraya-duyarli
yeniden-dene | bir=1 iki=2 ab=2 ba=2 -> yinelemeye-duyarli
rozet-artir | bir=4 iki=5 ab=5 ba=5 -> yinelemeye-duyarli
tercih-yaz | bir=koyu iki=koyu ab=acik ba=koyu -> siraya-duyarli
gunluge-ekle | bir=["giris"] iki=["giris", "giris"] ab=["giris", "cikis"] ba=["cikis", "giris"] -> cift-duyarli
saglam: 1
yinelemeye-duyarli: 2
siraya-duyarli: 2
cift-duyarli: 1
```

`onbellege-yaz` sağlam: aynı anahtarı iki kez yazmak anahtar kümesini değiştirmez ve iki anahtarın sırası sonucu değiştirmez, çünkü küme sıralı basılır. `anahtarli-gonderim` sıraya duyarlı: etkisizlik anahtarı aynı isteğin ikinci gelişini yok sayar, bu yüzden `bir` ile `iki` aynıdır; ama `k-41` ile `k-42` farklı isteklerdir ve varış sırası listenin sırasını belirler. `yeniden-dene` yinelemeye duyarlı: sayaç her uygulamada artar. `rozet-artir` de yinelemeye duyarlı, aynı nedenle. `tercih-yaz` sıraya duyarlı: son yazan kazanır. `gunluge-ekle` ikisine birden duyarlı: satır iki kez eklenir ve iki satırın sırası değişir.

**AG1.** Bu ölçüm altı işlemin her birini tek bir başlangıç durumundan kurar. Başka bir başlangıç durumunda bir işlemin sınıfı değişebilir.

**AG2.** `onbellege-yaz` anahtarları sıralı küme olarak tutar. Eklenme sırasını koruyan bir önbellek ayrıca ölçülmelidir.

Dağılım bir bulgu değildir. Bir sağlam, iki yinelemeye duyarlı, iki sıraya duyarlı ve bir çift duyarlı işlem çıkması işlemleri biz seçtiğimiz için çıktı. Başka altı işlemle başka bir dağılım çıkardı.

## Etkisizlik Neyi Çözer

`anahtarli-gonderim` bu dersin en öğretici satırıdır, çünkü yinelemeyi çözen şeyin sırayı çözmediğini gösterir. HTTP'de aynı isteği art arda göndermenin sonucu değiştirmediği yöntemlere **etkisiz** (idempotent) denir; `GET` ve `PUT` böyledir, `POST` değildir. Sunucu bir isteğe kimlik veren bir **etkisizlik anahtarı** kabul ederse, yeni bir sipariş yaratan `POST` da yinelemeye dayanıklı olabilir: aynı anahtarla gelen ikinci istek yok sayılır.

Ölçümde bu tam böyle çıktı. `bir` ile `iki` aynı: yineleme çözüldü. Ama iki farklı anahtar olduğunda sıra hâlâ sonucu belirliyor: `ab` ile `ba` aynı değil. **Etkisizlik yinelemeyi çözer, sırayı çözmez.** Sırayı çözmek için başka bir araç gerekir: sürüm numarası, birleştirilebilir bir veri yapısı ya da sunucuda sıralama. Sonraki derslerde bunların bir kısmını ölçeceğiz.

## Gerçek Uygulamada Nerede Görülür

Ölçülen sıra duyarlılığı yapay bir laboratuvar durumu değildir. Bir ekran açılırken profil isteği gönderir; kullanıcı aşağı çekip yenilediğinde ikinci bir istek daha çıkar. İki yanıt ağdan ters sırayla dönerse ekrana en son gelen yazılır ve bu, eski isteğin yanıtı olabilir. Sonuç, kullanıcının az önce yaptığı bir değişikliğin ekranda sessizce geri alınmış görünmesidir. Hata kaydında bunun izi yoktur: hiçbir istek başarısız olmamıştır, her yanıt kendi başına doğrudur.

Yeniden deneme ise yineleme duyarlılığının tipik örneğidir. Sipariş gönderen istek zaman aşımına uğradığında istemci yanıtın gelmediğini görür; ama sunucu isteği almış ve siparişi kaydetmiş olabilir. İstemci aynı isteği yeniden gönderirse sunucuda ikinci bir sipariş oluşur. Ölçümün `iki` sütunu tam olarak bu durumu gösterir: `yeniden-dene` satırında işlem yinelendiğinde sayı bir daha artmıştır.

Başlangıç durumu meselesi daha da sinsidir. Aynı işlem, boş bir önbellekle açılan bir uygulamada ve önceki oturumdan dolu kalmış bir önbellekle açılan bir uygulamada farklı davranabilir. Aşağıdaki sınırlayıcı ölçüm bunu gösterir: aynı işlem çifti, başlangıç durumuna göre farklı sınıfa düşer. Bu yüzden bir isteğin yinelemeye dayanıklı olup olmadığı yalnız isteğe bakılarak söylenemez; işlemin hangi durumdan koştuğu da sorulur.

## Yanlış Kurulum ve Düzeltmesi

En yaygın yanlış kurulum, yeniden denemeyi isteğin türüne bakmadan her başarısız isteğe uygulamaktır. Profili `GET` ile okumak yinelenince bir şey bozmaz; ama aynı kural sipariş yaratan bir `POST` isteğine de uygulanırsa her zaman aşımı ikinci bir sipariş riski taşır.

Düzeltme iki adımdır. Birincisi, yinelemeye duyarlı isteğe bir etkisizlik anahtarı eklemektir: istemci anahtarı ilk denemede üretir ve her yeniden denemede aynısını gönderir, sunucu da aynı anahtarla gelen ikinci isteği yok sayar. Ölçümdeki `anahtarli-gonderim` satırında `bir` ile `iki` sütunlarının aynı çıkması bunun karşılığıdır. İkincisi, sıraya duyarlı yanıtlarda ekranın yalnız en son gönderilen isteğin yanıtını kabul etmesidir; eski bir isteğin geç gelen yanıtı yok sayılır. Bu, sıra sorununu ortadan kaldırmaz ama sırayı tek bir yerde görünür kılar; sıraya duyarlı bir işlemin güvenli hâle gelmesinin yolu çoğu zaman sıranın artık değişememesidir.

Rozet için düzeltme farklıdır. Sayıyı yerelde artırmak yerine sunucunun bildirdiği mutlak değeri yazmak, işlemi yinelemeye karşı korur: aynı değer ikinci kez yazılsa da sonuç değişmez. Mutlak değerin de kendi sorusu vardır, iki değer ters sırayla gelirse eskisi kalır; bunu bu derste ayrıca ölçmedik, yalnız yönünü söylüyoruz.

## KAYNAK Parçayı Okumak

Dersteki iki KAYNAK parça çalıştırılmaz, çünkü ikisi de ağa çıkar ve sonuçları sunucuya bağlıdır. Parçalar yine de okunmaya değerdir. `fetchProfile` bir okumadır: aynı isteği iki kez göndermek sunucudaki durumu değiştirmez ve modeldeki karşılığı `onbellege-yaz` satırıdır. `send(order:session:)` ise yeni bir kayıt yaratan bir `POST` isteğidir ve modeldeki `placeOrder` işlevine denk düşer. Model ile gerçek arasındaki fark, gerçek istekte yinelemenin istemcinin bilgisi dışında da olabilmesidir: bağlantı koptuğunda istemci isteğin sunucuya ulaşıp ulaşmadığını bilemez. Bu fark ölçülen sınıfı değiştirmez; yalnız yinelemenin nereden çıkabileceğini söyler.

Parçalardaki durum kodu denetimi de önemlidir. `fetchProfile` yalnız 200'ü kabul eder; `send(order:session:)` ise durum kodunu olduğu gibi döndürür ve kararı çağırana bırakır. Zaman aşımında hiç yanıt gelmez ve istemci isteğin işlenip işlenmediğini bilmez. Bu durumu sunucunun açıkça reddettiği bir istekle aynı hata saymak, yeniden denemenin yinelemeye duyarlı bir isteği ikinci kez göndermesine yol açar.

## Sık Yapılan Hatalar

- Her istek için yeni bir oturum kurmak. Yapılandırma ve bağlantılar paylaşılmaz.
- Yanıtın `HTTPURLResponse` olup olmadığını ve durum kodunu denetlememek. Sunucu hata kodu döndürse de çağrı hata fırlatmaz.
- Büyük bir gövdeyi veri görevi ile belleğe almak. İndirme görevi dosyaya yazar.
- Yinelenen bir kullanıcı eylemini ağ katmanında yok saymak. Düğmeye iki kez basmak iki istek doğurur; yineleme sorusu istemcide ve sunucuda ayrı ayrı cevaplanır.
- Etkisizlik anahtarını sıra sorununu da çözdüğünü varsayarak kullanmak.

## Ölçülen Sonucun Yorumu

Altı işlemin altısı da bir ağ katmanında karşımıza çıkabilecek işlerdi ve dört kategoriye dağıldı. Önemli olan dağılım değil, bir işlemin sınıfının iki sorudan çıktığıdır: yinelenirse ne olur, sırası değişirse ne olur. Bu iki sorunun cevabı bir işlemden öbürüne değişir ve çoğu zaman işlemin adından çıkarılamaz. `placeOrder` ile `badge` aynı gövdeye sahip olsa da biri bir sipariş, öbürü bir sayaçtır; kullanıcı için sonuçları çok farklıdır, kural için aynıdır.

Ağ katmanında bu ayrım üç yerde işe yarar. Yeniden denemede: yinelemeye duyarlı bir işlem denenince ikinci kayıt doğurur. Yanıt sırasında: sıraya duyarlı bir işlem iki yanıt ters gelirse bayat değer bırakır. Önbellekte: sağlam bir işlem hata riski taşımadan tekrar edilebilir. Sonraki beş ders bu üç yeri sırayla ölçer.

## Sınırlayıcı Ölçüm

Bir işlemin sınıfı yalnız işleme değil başlangıç durumuna da bağlıdır. Aşağıda aynı iki işlem, `x * 2` ve `x * 3`, iki başlangıçtan çalıştırılır.

```swift
// main.swift — ayni iki islem, iki baslangic
let doubled = replay("sifirdan", from: 0, { $0 * 2 }, then: { $0 * 3 }).report()
let fromOne = replay("birden", from: 1, { $0 * 2 }, then: { $0 * 3 }).report()
countSensitivities([doubled, fromOne])
```

```
sifirdan | bir=0 iki=0 ab=0 ba=0 -> saglam
birden | bir=2 iki=4 ab=6 ba=6 -> yinelemeye-duyarli
saglam: 1
yinelemeye-duyarli: 1
siraya-duyarli: 0
cift-duyarli: 0
```

Sıfırdan başlayınca iki işlem de sıfırı sıfır bırakır ve satır sağlamdır. Birden başlayınca ikinci uygulama değeri değiştirir ve satır yinelemeye duyarlıdır. İki sıra bu işlem çiftinde her iki başlangıçta da aynı sonucu verir, çünkü çarpmalar yer değiştirir. Aynı işlem, başlangıç durumu değişince başka sınıfa düştü.

**AG3.** Bu ölçüm yalnız sıfır ve bir başlangıçlarını karşılaştırır. Başka başlangıçlar ölçülmedi ve sınıf onlarda da değişebilir.

## Özet

- Ağ istemcisi üç parçadan kurulur: yapılandırma davranışı söyler, oturum o davranışla çalışır, görev tek bir aktarımı yürütür.
- Veri görevi yanıtı belleğe alır, yükleme görevi gövdeyi ayrı gönderir, indirme görevi yanıtı dosyaya yazar.
- Duyarlılık iki soruyla ölçülür: yinelenirse ve sırası değişirse sonuç değişir mi.
- Altı işlem dört kategoriye ayrıldı: bir sağlam, iki yinelemeye duyarlı, iki sıraya duyarlı, bir çift duyarlı.
- Etkisizlik yinelemeyi çözer, sırayı çözmez; aynı işlem başlangıç durumu değişince başka sınıfa düşebilir.
- Bu dersin duyarlılığı: **dördü de**.

## Sonraki Adım

Ağdan dönen yanıt bir bayt dizisidir; uygulamanın ihtiyacı ise tipli bir kayıttır. Sıradaki derste JSON Kodlama ve Çözme, bir yanıtı kayda çevirmenin kendisinin de sıraya duyarlı olabileceğini ölçecek: iki sürümlü bir kaydın hangi sırayla yazıldığı depodaki değeri belirler.
