---
title: WebSocket
source: 'https://academia.sh/tr/kurslar/uygulama-protokolleri/websocket'
course: 'Uygulama Katmanı Protokolleri'
language: tr
updated: '2026-08-17T18:07:02+00:00'
license: 'CC BY-SA 4.0'
---

# WebSocket

Yükseltme el sıkışmasının bir rejim değişimi olması: 101'den sonra aracının tünele düşmesi, iki yüz kararın tamamının uçlara taşınması, çerçevenin taşıdığı alanların hiçbirinden karar çıkmaması ve gizlemenin bayt cinsinden bedeli.

Önceki ders taşıma zarfını kapattı ve aracının kaybını saydı: iki yüz kararın yüz altmışı
ortadan uçlara taşındı, geriye yalnız konak adından çıkarılan kırk karar kaldı. Kaybın
büyüklüğüne karşın bir şey yerinde durmuştu. Bağlantı hâlâ **istek–yanıt** biçimindeydi:
istemci bir ileti gönderiyor, sunucu bir ileti döndürüyor, sıra yeniden istemciye geçiyordu.
Aracı iletinin içini göremese de kaç ileti olduğunu, her birinin nerede başlayıp nerede
bittiğini biliyordu.

Bu dersin sorusu şudur: bağlantının **biçimi** de değişirse ne kalır? **WebSocket** tam
bunu yapar. Olağan bir HTTP isteğiyle başlar, tek bir yanıtla kendi çerçeve biçimine geçer
ve o noktadan sonra bağlantı üzerinde ne istek satırı, ne başlık alanı, ne de durum kodu
vardır. Aynı bağlantı üzerinde iki uç da birbirine istediği anda yazabilir.

## Yükseltme El Sıkışması

Bağlantı, aracının okuyabildiği tek bir alışverişle açılır. İstemci olağan bir GET isteği
gönderir; isteği ayıran şey, bağlantının **yükseltileceğini** bildiren başlık alanlarıdır.
Buna **yükseltme el sıkışması (upgrade handshake)** denir.

```text
GET /akis HTTP/1.1
Host: istasyon.ornek
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Key: <on altı baytlık rastgele değerin base64 kodu>
Sec-WebSocket-Version: 13

HTTP/1.1 101 Switching Protocols
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Accept: <anahtar ile belirtimdeki sabitin özetinin base64 kodu>
```

`Sec-WebSocket-Accept` değeri, istemcinin gönderdiği anahtardan belirtimde sabitlenmiş bir
dizgeyle birlikte türetilir. Bu türetmenin **hiçbir gizlilik işlevi yoktur**; yalnız yanıtı
gerçekten yükseltmeyi anlamış bir uçtan geldiğine bağlar ve aradaki bir önbelleğin eski bir
yanıtı yeniden oynatmasını ayırt eder. Kabul değerinin hesabı ve sunucu tarafındaki
kurulumu **Önbellekleme, Kuyruklar ve Eşzamansız İşleme** kursunda yazıldı; orada ölçülen
şey sunucunun her bağlantı için tuttuğu durumdu. **Burada ölçülen, aradaki aracının
iletiden alabildiği karardır.**

Belirleyici satır `101 Switching Protocols`'tür. 100 sınıfı bir ara yanıttır: istek
sürüyor, sonuç henüz yok. 101 ise sürecin sonucunu değil, **artık başka bir protokol
konuşulacağını** bildirir. Bu satırdan sonra bağlantı üzerinde HTTP yoktur.

## Aracının Ölçüsü

Kursun kurgusu bu derste de aynıdır. Kuzey Yamaç ölçüm istasyonu bir istemciye ölçüm sunar;
arada uygulamayı bilmeyen bir aracı durur ve yalnız iletide yazana bakarak karar verir.

- **DG1.** Kırk alışveriş ve alışveriş başına beş karar ölçülür: `saklanabilir`,
  `paylasilabilir`, `taze`, `yinelenebilir`, `yonlendirilebilir`. Küme iki yüz karardır.
- **DG2.** Kâhin — kararın gerçeği — kurguyu biz ürettiğimiz için bilinir. Aracının verdiği
  yanıt bu gerçekle karşılaştırılır; sormak zorunda kaldığı her alışveriş bir **tur
  borcudur**.
- **DG3.** `açık` rejimde aracı yöntem, yol, konak adı, kişiye özel imi ve doğrulayıcıyı
  okur. `tünel` rejimde hiçbirini okumaz.
- **DG4.** Bir WebSocket bağlantısının üzerinde kırk alışverişin tamamı taşınır; bağlantı
  bir kez açılır ve kapanmaz.
- **DG5.** Dökümler ve çerçeve boyları kurgudur; ders içinde hiçbir bağlantı açılmaz.

```python
TOHUM = 20260809
GUVENLI = {"GET", "HEAD"}
ETKISIZ = {"GET", "HEAD", "PUT", "DELETE"}
YONTEMLER = ["GET", "GET", "GET", "HEAD", "POST", "PUT", "DELETE"]
YOLLAR = ["/olcum/kuzey", "/olcum/yamac", "/ozet", "/kayit", "/oturum"]
KARARLAR = ("saklanabilir", "paylasilabilir", "taze", "yinelenebilir",
            "yonlendirilebilir")
GORUNUR = {"açık": ("yontem", "yol", "konak", "ozel_imi", "dogrulayici"),
           "tünel": ()}


def uretec(tohum: int):
    d = tohum % 2147483646 + 1

    def r(n: int) -> int:
        nonlocal d
        d = (d * 48271) % 2147483647
        return d % n
    return r


def alisverisler(sayi: int = 40) -> list[dict]:
    r, liste = uretec(TOHUM), []
    for i in range(sayi):
        yontem, yol = YONTEMLER[r(7)], YOLLAR[r(5)]
        ozel = yol == "/oturum" or r(5) == 0
        degisti = r(3) == 0
        liste.append({"no": i + 1, "yontem": yontem, "yol": yol, "ozel": ozel,
                      "degisti": degisti, "ozel_imi": ozel and r(4) != 0,
                      "damga_atladi": degisti and r(6) == 0})
    return liste


def kahin(a: dict) -> dict:
    return {"saklanabilir": a["yontem"] in GUVENLI and not a["ozel"],
            "paylasilabilir": not a["ozel"], "taze": not a["degisti"],
            "yinelenebilir": a["yontem"] in ETKISIZ, "yonlendirilebilir": True}


def ileti(a: dict, gorunur: tuple) -> dict:
    tam = {"yontem": a["yontem"], "yol": a["yol"], "konak": "istasyon.ornek",
           "ozel_imi": a["ozel_imi"],
           "dogrulayici": a["degisti"] and not a["damga_atladi"]}
    return {k: v for k, v in tam.items() if k in gorunur}


def araci(i: dict) -> dict:
    k = {}
    if "yontem" in i:
        k["yinelenebilir"] = i["yontem"] in ETKISIZ
    if "ozel_imi" in i:
        k["paylasilabilir"] = not i["ozel_imi"]
        if "yontem" in i:
            k["saklanabilir"] = i["yontem"] in GUVENLI and not i["ozel_imi"]
    if "dogrulayici" in i:
        k["taze"] = not i["dogrulayici"]
    if "konak" in i:
        k["yonlendirilebilir"] = True
    return k


def olc(ad: str) -> tuple:
    dogru = yanlis = alinamayan = tur = bayt = 0
    for a in alisverisler():
        gercek, verilen = kahin(a), araci(ileti(a, GORUNUR[ad]))
        eksik = False
        for karar in KARARLAR:
            if karar not in verilen:
                alinamayan, eksik = alinamayan + 1, True
            elif verilen[karar] == gercek[karar]:
                dogru += 1
            else:
                yanlis += 1
        tur += 1 if eksik else 0
        bayt += sum(len(k) + len(str(v)) + 4
                    for k, v in ileti(a, GORUNUR[ad]).items())
    return dogru, yanlis, alinamayan, tur, bayt


print(f"{'rejim':<7s} {'doğru':>6s} {'yanlış':>7s} {'alınamayan':>11s} "
      f"{'tur borcu':>10s} {'bayt':>6s}")
for ad in ("açık", "tünel"):
    d, y, e, t, b = olc(ad)
    print(f"{ad:<7s} {d:6d} {y:7d} {e:11d} {t:10d} {b:6d}")
```

```
rejim    doğru  yanlış  alınamayan  tur borcu   bayt
açık       195       5           0          0   3524
tünel        0       0         200         40      0
```

Yükseltme öncesinde aracı kırk alışverişin tamamını okuyordu ve iki yüz kararın iki yüzünü
kendi başına alıyordu; sunucuya fazladan hiçbir tur atmıyordu. Yükseltmeden sonra elinde
kalan karar sayısı **sıfırdır** ve kırk alışverişin kırkı birer tur borcuna dönüşür.

İki satır arasındaki fark, taşıma zarfının farkından da büyüktür. Zarf rejiminde aracı
konak adını görüyor ve `yonlendirilebilir` kararını kırk kez alabiliyordu; tünelde onu da
kaybeder. Bunun tek nedeni şifreleme değildir: **101'den sonra bağlantı üzerinde
`yonlendirilebilir` kararını besleyecek bir alan yeniden görünmez.** Yönlendirme kararı
bağlantının kurulduğu anda bir kez alınmış, sonrasında dondurulmuştur.

## El Sıkışma, Aracının Okuduğu Son İletidir

Yükseltme el sıkışması bir istisna değil, bir **sınırdır**. Bağlantı üzerinde taşınan
iletileri tek tek aynı karar işlevinden geçirmek bunu gösterir.

```python
GUVENLI = {"GET", "HEAD"}
ETKISIZ = {"GET", "HEAD", "PUT", "DELETE"}


def araci(i: dict) -> dict:              # önceki bloktaki karar işlevinin aynısı
    k = {}
    if "yontem" in i:
        k["yinelenebilir"] = i["yontem"] in ETKISIZ
    if "ozel_imi" in i:
        k["paylasilabilir"] = not i["ozel_imi"]
        if "yontem" in i:
            k["saklanabilir"] = i["yontem"] in GUVENLI and not i["ozel_imi"]
    if "dogrulayici" in i:
        k["taze"] = not i["dogrulayici"]
    if "konak" in i:
        k["yonlendirilebilir"] = True
    return k


BAGLANTI = [
    ("yükseltme isteği", {"yontem": "GET", "yol": "/akis",
                          "konak": "istasyon.ornek", "yukseltme": "websocket"}),
    ("101 yanıtı", {"yukseltme": "websocket", "kabul": "kurgu-deger"}),
    ("metin çerçevesi", {"fin": 1, "opkod": 1, "maskeli": True, "uzunluk": 88}),
    ("kapat çerçevesi", {"fin": 1, "opkod": 8, "maskeli": True, "uzunluk": 2}),
]

print(f"{'ileti':<18s} {'alan':>5s} {'karar':>6s}  alınan kararlar")
for ad, m in BAGLANTI:
    k = araci(m)
    print(f"{ad:<18s} {len(m):5d} {len(k):6d}  {', '.join(k) or '-'}")
```

```
ileti               alan  karar  alınan kararlar
yükseltme isteği       4      2  yinelenebilir, yonlendirilebilir
101 yanıtı             2      0  -
metin çerçevesi        4      0  -
kapat çerçevesi        4      0  -
```

Yükseltme isteği beş kararın **ikisini** verir: yöntem alanından `yinelenebilir`, konak
alanından `yonlendirilebilir`. Kalan üçü yanıtın alanlarını gerektirir ve o yanıt hiç
gelmez — gelen yanıt 101'dir ve kişiye özel imi de doğrulayıcı da taşımaz. Bu iki karar,
bağlantının ömrü boyunca aracının elindeki tek karardır.

## Çerçeve Görünse de Karar Çıkmazdı

Yukarıdaki tabloda çerçeveler **dört alan** taşıyor ve yine de sıfır karar üretiyor. Bu,
kaybın tek başına gizlemeden gelmediğini söyler. Çerçevenin yerleşimi belirtimde
sabitlenmiştir ve taşıdığı alanların hepsi **taşıma hakkındadır**, ileti hakkında değil.

```text
 0                   1                   2                   3
 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+-+-+-+-+-------+-+-------------+-------------------------------+
|F|R|R|R| işlem |M|   uzunluk   |  genişletilmiş uzunluk (16 bit)|
|I|S|S|S| kodu  |A|    (7 bit)  |  uzunluk alanı 126 ise         |
|N|V|V|V| (4bit)|S|             |                               |
| |1|2|3|       |K|             |                               |
+-+-+-+-+-------+-+-------------+-------------------------------+
|      maske anahtarı (32 bit, maske biti 1 ise)                |
+---------------------------------------------------------------+
|                        yük verisi                             |
+---------------------------------------------------------------+
```

**Çerçeveleme (framing)**, bayt akışını ileti sınırlarına bölmenin kuralıdır. Uzunluk alanı
yedi bittir; 125'e kadar olan yükler doğrudan yazılır, 126 değeri iki baytlık, 127 değeri
sekiz baytlık bir uzunluk alanının izlediğini bildirir. Maske biti kurulduğunda dört
baytlık bir anahtar eklenir ve yük bu anahtarla katlanır. Maske istemci yönünde zorunludur.

```python
ISLEM = {0: "sürdürme", 1: "metin", 2: "ikili", 8: "kapat", 9: "yokla",
         10: "karşılık"}


def basluk_boyu(yuk: int, maskeli: bool) -> int:
    n = 2                                  # FIN, RSV, işlem kodu, maske biti, uzunluk
    n += 8 if yuk > 65535 else 2 if yuk > 125 else 0   # genişletilmiş uzunluk
    n += 4 if maskeli else 0               # maske anahtarı yalnız istemci yönünde
    return n


print("işlem kodları:", ", ".join(f"{k}={v}" for k, v in ISLEM.items()))
print()
print(f"{'yük':>6s} {'yön':<15s} {'başlık':>7s} {'toplam':>7s} {'başlık payı':>12s}")
for yuk, maskeli in ((2, True), (88, True), (125, True), (126, True),
                     (88, False), (70000, False)):
    b = basluk_boyu(yuk, maskeli)
    yon = "istemci→sunucu" if maskeli else "sunucu→istemci"
    print(f"{yuk:6d} {yon:<15s} {b:7d} {b + yuk:7d} {b / (b + yuk):12.3%}")
```

```
işlem kodları: 0=sürdürme, 1=metin, 2=ikili, 8=kapat, 9=yokla, 10=karşılık

   yük yön              başlık  toplam  başlık payı
     2 istemci→sunucu        6       8      75.000%
    88 istemci→sunucu        6      94       6.383%
   125 istemci→sunucu        6     131       4.580%
   126 istemci→sunucu        8     134       5.970%
    88 sunucu→istemci        2      90       2.222%
 70000 sunucu→istemci       10   70010       0.014%
```

Açık rejimde aracının okuduğu 3524 bayt kırk alışverişe bölündüğünde alışveriş başına 88
bayt düşer; çerçeve başlığı aynı yükü sunucu yönünde **iki bayta** sarar. Kazanç gerçektir
ve küçük iletilerde belirgindir. Ama karşılığı da tam olarak ölçülmüştür: 88 baytın iki
yüz kararı, iki baytın sıfır kararıdır.

Bu, kursun ikinci iddiasının tersten okunuşudur. Orada alan eklemenin karar
kazandırmadığını görmüştük: üç alan daha, bayt artışı, sıfır yeni karar. Burada aynı şey
öbür yönde çıkıyor — **baytı kısmak da karar kaybettirmez; kararı kaybettiren, alanın
kendisinin ortadan kalkmasıdır.** Bayt ile karar arasında bir bağ yoktur; bağ, alan ile
karar arasındadır.

## Aracının Elinde Kalan Tek Kol

Aracı karar alamaz ama bağlantıyı taşımaya devam eder ve taşıdığı şey üzerinde tek bir
yetkisi kalır: **onu kapatmak.** Boşta duran bir bağlantıyı belirli bir süre sonra kesmek —
**boşta bekleme süresi (idle timeout)** — bir aracının açık tuttuğu bağlantı sayısını
sınırlamak için başvurduğu olağan kuraldır. Açık
rejimde bu kural zararsızdır; istek–yanıt biçiminde bağlantı zaten iletiler arasında
boşalır ve yeniden kurulabilir. Tünelde ise bağlantının kendisi durumun taşıyıcısıdır:
kesilen bağlantı, iki ucun üzerinde anlaştığı ne varsa onu da götürür.

Belirtim buna iki **denetim çerçevesi** ile karşılık verir. **Yokla (ping)** ve **karşılık
(pong)** işlem kodları veri taşımaz; tek işlevleri bağlantı üzerinde bayt akıtmak ve iki
ucun ayakta olduğunu doğrulamaktır. Bunlar aracıya bir şey **anlatmaz** — tabloda ölçüldüğü
gibi karar üretmezler — yalnız aracının boşta sayacını sıfırlar. Aracının iletiden
alabildiği karar sıfırken bile bağlantı üzerinde bir tasarım kısıtı doğurması, bu dersin
sessiz sonucudur.

Bir alan daha yükseltme isteğinde taşınabilir: `Sec-WebSocket-Protocol` başlığı, çerçevelerin
hangi kurala göre yorumlanacağını adlandırır ve sunucu seçtiği adı yanıtta yineler. Bu ad,
aracının bağlantı üzerinde göreceği **anlamla ilgili tek işarettir** ve o da yalnız açılış
alışverişinde bir kez geçer. Sonrasında aracı, adı bildiği ama içeriğini okuyamadığı bir
akışı iletir.

## Kararların Taşındığı Yer

İki yüz karar yok olmaz, taşınır. Tünel rejiminde her biri artık bir uçta alınmak
zorundadır ve bu, uygulamaya yazılacak iş demektir.

| Karar | Açık rejimde kimin | Tünelde kimin | Uçta ne gerektirir |
|---|---|---|---|
| `yinelenebilir` | aracı | gönderen uç | ileti kimliği ve yineleme kuralı |
| `saklanabilir` | aracı | alan uç | uygulama içi kopya ve geçersizleştirme |
| `paylasilabilir` | aracı | sunucu | kapsamı çerçevenin içine yazmak |
| `taze` | aracı | alan uç | sürüm ya da sayaç alanı |
| `yonlendirilebilir` | aracı | bağlantı kurulumu | açılışta bir kez, sonra sabit |

Tablonun sağ sütunu, WebSocket kullanan bir uygulamanın **yeniden yazmak zorunda kaldığı**
şeydir. HTTP'de bu kararların hepsi belirtimin içinde tanımlıydı ve yolun üzerindeki
herhangi bir aracı onları uygulayabiliyordu. Çerçeve biçiminde tanımlı olan tek şey
taşımadır; anlamı taşıyan alanları iki uç kendi aralarında kararlaştırır.

Bunun bir kusur olmadığını da kayda geçirmek gerekir. Ölçüm tablosunda tünel satırının
`yanlış` sütunu **sıfırdır**. Aracı hiçbir yanlış karar almaz, çünkü hiç karar almaz. Açık
rejimin beş yanlışı, bildirime inanmanın bedeliydi; tünel o bedeli de ödemez. Seçim, kararı
yolun üzerinde ucuza almakla, kararı uçta pahalıya ama tam almak arasındadır.

## Özet

- Yükseltme el sıkışması olağan bir HTTP alışverişidir; `101 Switching Protocols` satırı
  bir sonucu değil, bağlantı üzerinde artık başka bir protokol konuşulacağını bildirir.
- Aynı kırk alışveriş açık rejimde 195 doğru ve 5 yanlış kararla, hiç tur borcu doğurmadan
  ölçülürken tünel rejiminde 200 kararın 200'ü alınamaz ve kırk tur borcu doğar.
- Aracının bağlantı boyunca okuyabildiği tek ileti yükseltme isteğidir ve o da beş kararın
  yalnız ikisini verir; 101 yanıtı ile çerçevelerin hiçbiri karar üretmez.
- Çerçeve başlığı iki ile on dört bayt arasındadır ve taşıdığı alanlar — FIN, işlem kodu,
  maske, uzunluk — taşıma hakkındadır; bayt kısmak karar kaybettirmez, alanın yok olması
  kaybettirir.
- Taşınan iki yüz karar uçların işine dönüşür; buna karşılık tünel rejiminde alınan yanlış
  karar sayısı sıfırdır.

## Sonraki Adım

Tünel, aracıyı iletiden tümüyle kopardığı için iki yüz kararın tamamını uçlara taşıdı.
Ama sunucunun istemciye kendiliğinden veri göndermesi için bağlantının biçimini değiştirmek
zorunlu mudur? Yanıtın gövdesi bir kerede yazılmak zorunda değildir: açık tutulup üzerine
parça parça yazılabilir ve bu, HTTP'nin kendi kuralları içinde kalır. Sonraki ders bu
seçeneği ölçer — aracı iletiyi hâlâ okuyabildiğinde elinde kaç karar kalır ve okuyabilmenin
kendisi ne getirir.
