---
title: 'Gerçek Zamanlı İletişim'
source: 'https://academia.sh/tr/kurslar/go-uygulamalar/gercek-zamanli-iletisim'
course: 'Standart Kütüphane ve Ağ Uygulamaları'
language: tr
updated: '2026-08-23T16:55:13+00:00'
license: 'CC BY-SA 4.0'
---

# Gerçek Zamanlı İletişim

Uzun süreli bir bağlantı, gerçek bağlantı noktası açılmadan, io.Reader ve io.Writer'ı gerçekleştiren bir bayt arabelleğiyle modelleniyor. Sunucu üç mesajı art arda, aralarında bir istek beklemeden itiyor. Çerçevesiz bir akışta üç itme tek bir bayt dizisine karışıyor ve sabit boyutlu okumalar hiçbir mesajı doğru ayıramıyor (0/3); uzunluk ön ekiyle çerçevelenen akışta üçü de doğru geri ayrışıyor (3/3). Sınırlayıcı ölçüm: çerçeveleme mesajın nerede bittiğini veriyor, içindeki değerin tipini vermiyor.

Önceki ders iki tarafın tek bir alışveriş yaptığı bir sınırı ölçtü: gönderen bir kez yazdı,
alıcı bir kez okudu, iki taraf hiç aynı anda çalışmadı. Bu ders bu varsayımı kaldırıyor.
Bir bağlantı **uzun sürdüğünde** — açılıp kapanmadan art arda birçok geçişi taşıdığında —
sınırı geçen tek şey artık bir bayt dizisinin **içeriği** değil, o bayt dizisinin **nerede
bittiği**. Kursun sorusu aynı kalıyor, ama bu kez sorulduğu yer bir tek değerin sınırı değil,
zaman içinde uzayan bir akış.

Gerçek bir ağ bağlantısı bu derste hiç açılmıyor. Bunun yerine bir `bytes.Buffer`
kullanılıyor — standart kitaplığın bayt arabelleği, `io.Reader` ve `io.Writer`
arayüzlerinin ikisini de gerçekleştiriyor, tıpkı gerçek bir ağ bağlantısının
gerçekleştireceği gibi. Bu ders bağlantıyı bu arabellekle **modelliyor**; ölçülen kod
yalnızca bu iki arayüzü kullanıyor, ve aynı kod gerçek bir bağlantı üzerinde de değişmeden
çalışırdı, çünkü ikisi de aynı sözleşmeyi karşılıyor. Adres, bağlantı noktası ve süre
hiçbir yerde basılmıyor, çünkü hiçbiri bu modelde hiç var olmuyor.

Bu, kursun HTTP Sunucusu ve HTTP İstemcisi derslerinin izlediği stratejinin bir devamı.
O iki ders de gerçek bir bağlantı noktası açmadan, bir test yardımcısıyla ölçüm yapmıştı;
burada aynı fikir farklı bir katmanda uygulanıyor. HTTP'nin kendi test yardımcısı tek bir
istek–yanıt çiftini modellemek için yeterliydi, çünkü HTTP'nin sınırı zaten netti — bir
istek başlar, bir yanıt biter, bağlantı kapanır ya da bir sonraki isteğe hazır bekler. Uzun
süreli bir bağlantıda böyle bir sınır yok; sunucu istediği zaman yazabiliyor, istemci
istediği zaman okuyabiliyor, ve tam da bu yüzden bu dersin ölçtüğü şey "bir isteğin ne
taşıdığı" değil, "akışın neresinde bir mesajın bittiği".

Bu fark ölçüm aracının seçimini de belirliyor. HTTP dersleri gerçek bir istemci ve sunucu
nesnesi kurup aralarında tek bir alışverişi ölçtü; bu ders tek bir paylaşılan bayt
arabelleğini hem yazan hem okuyan tarafın ortak zemini olarak kullanıyor, çünkü ölçülmek
istenen şey iki ayrı tarafın davranışı değil, aradaki **akışın kendisinin** yapısı — kaç
mesajın art arda geldiği ve bu mesajların akış içinde nasıl ayırt edildiği.

## Bağlantı Modelleniyor: Aynı Arayüz, Farklı Somut Uygulama

**AG33.** `bytes.Buffer`, `io.Reader` ile `io.Writer`ı gerçekleştiriyor — tam olarak Go'nun
standart kitaplığının bir ağ bağlantısı için tanımladığı arayüzlerin ikisi. Sistem ve Veri
Erişimi konusunun ilk dersi bu iki arayüzü dosya, arabellek ve dizgi arasında paylaşan bir
olguyu ölçecek; bu ders aynı olgunun bağlantı tarafını, ağ hiç açılmadan, önceden kuruyor.
Aşağıdaki yordamlar bir mesajı önce dört baytlık büyük uçlu bir uzunluk öneki, sonra
mesajın kendi baytlarıyla yazıyor; okuma tarafı önce bu dört baytı okuyup uzunluğu
öğreniyor, sonra tam o kadar baytı istiyor.

Bu modelin bir sınırı var ve açıkça yazılması gerekiyor: gerçek bir ağ bağlantısında bir
`Write` çağrısının karşı tarafa ne zaman ulaştığı, tek bir çağrının kaç parçaya bölünerek
ilerlediği, gecikme ve sıralama gibi etkenlere bağlı olabilir. Bu ders bunların hiçbirini
modellemiyor — `bytes.Buffer` üzerindeki bir yazma anında tamamlanıyor ve bir sonraki okuma
her zaman en güncel hâli görüyor. Ölçülen şey gecikme ya da sıralama değil, yalnızca
**çerçevelemenin kendisi**: bir akışın içine gömülü mesajların, akışı okuyan tarafından
nasıl ayrıştırılacağı. Bu dar kapsam bilinçli; genişletilmiş bir model, aynı çerçeveleme
kuralının üzerine kurulur, onu değiştirmez.

```go
// cerceveleme.go — uzun sureli baglantida cercevenin mesaj sinirini nasil tasidiginin olcumu
package main

import (
	"bytes"
	"encoding/binary"
	"fmt"
	"io"
)

func cerceveleYaz(baglanti *bytes.Buffer, mesaj string) {
	var uzunluk [4]byte
	binary.BigEndian.PutUint32(uzunluk[:], uint32(len(mesaj)))
	baglanti.Write(uzunluk[:])
	baglanti.WriteString(mesaj)
}

func cerceveOku(baglanti *bytes.Buffer) (string, bool) {
	var uzunluk [4]byte
	if _, err := io.ReadFull(baglanti, uzunluk[:]); err != nil {
		return "", false
	}
	boyut := binary.BigEndian.Uint32(uzunluk[:])
	govde := make([]byte, boyut)
	if _, err := io.ReadFull(baglanti, govde); err != nil {
		return "", false
	}
	return string(govde), true
}

func main() {
	itilenler := []string{"merhaba", "veri", "kapanis"}

	var cercevesizBaglanti bytes.Buffer
	for _, m := range itilenler {
		cercevesizBaglanti.WriteString(m)
	}
	fmt.Println("-- cercevesiz baglantida uc itme tek bir bayt dizisine karisiyor --")
	dogruAyrisan := 0
	for {
		parca := make([]byte, 6)
		n, err := cercevesizBaglanti.Read(parca)
		if n == 0 {
			break
		}
		parcaMetni := string(parca[:n])
		fmt.Printf("okunan parca: %q\n", parcaMetni)
		for _, m := range itilenler {
			if parcaMetni == m {
				dogruAyrisan++
			}
		}
		if err != nil {
			break
		}
	}
	fmt.Printf("dogru ayrisan mesaj: %d/%d\n", dogruAyrisan, len(itilenler))

	var cerceveliBaglanti bytes.Buffer
	for _, m := range itilenler {
		cerceveleYaz(&cerceveliBaglanti, m)
	}
	fmt.Println()
	fmt.Println("-- cerceveli baglantida uc itme de sunucunun yazdigi sinirla geri ayrisiyor --")
	dogruAyrisanCerceveli := 0
	for i := 0; i < len(itilenler); i++ {
		m, tamam := cerceveOku(&cerceveliBaglanti)
		if !tamam {
			break
		}
		fmt.Printf("okunan mesaj: %q\n", m)
		if m == itilenler[i] {
			dogruAyrisanCerceveli++
		}
	}
	fmt.Printf("dogru ayrisan mesaj: %d/%d\n", dogruAyrisanCerceveli, len(itilenler))

	var sayisalItme bytes.Buffer
	cerceveleYaz(&sayisalItme, "42")
	govde, _ := cerceveOku(&sayisalItme)
	fmt.Println()
	fmt.Println("-- sinirlayici olcum: cerceve dogru govdeyi veriyor ama govdenin tipini vermiyor --")
	fmt.Printf("govde: %q, tipi: %T\n", govde, govde)
}
```

```
-- cercevesiz baglantida uc itme tek bir bayt dizisine karisiyor --
okunan parca: "merhab"
okunan parca: "averik"
okunan parca: "apanis"
dogru ayrisan mesaj: 0/3

-- cerceveli baglantida uc itme de sunucunun yazdigi sinirla geri ayrisiyor --
okunan mesaj: "merhaba"
okunan mesaj: "veri"
okunan mesaj: "kapanis"
dogru ayrisan mesaj: 3/3

-- sinirlayici olcum: cerceve dogru govdeyi veriyor ama govdenin tipini vermiyor --
govde: "42", tipi: string
```

**AG34.** `itilenler` dizisindeki üç mesaj `cercevesizBaglanti` ile `cerceveliBaglanti`ya
art arda yazılıyor — aralarında hiçbir okuma, hiçbir "istek" yok. Bu, HTTP sunucusu ve
istemcisi derslerinde kurulan istek–yanıt kalıbından farklı: orada her yanıt bir isteği
takip ediyordu, burada sunucu tarafı üç kez de kendi inisiyatifiyle **itiyor**. Uzun süreli
bir bağlantının veri itme özelliği tam olarak bu — karşı taraftan bir tetikleyici gelmeden
yazabilme.

Sunucunun bir isteği nasıl karşıladığı Backend Geliştirme müfredatının sekiz kursunda kavram
olarak kuruldu ve burada tekrarlanmıyor; bu dersin payı yalnızca Go'nun standart
kitaplığındaki iki arayüzün (`io.Reader`, `io.Writer`) böyle bir itmeyi taşıyıp taşımadığı.
Yanıt burada evet: `bytes.Buffer.Write` çağrısı hiçbir isteğe bağlı değil, yalnızca
çağrıldığı anda arabelleğin sonuna ekleme yapıyor — bu, bir sunucunun bir istemciye önceden
haber vermeden veri göndermesinin, standart kitaplık düzeyinde ne kadar sade bir işlem
olduğunu gösteriyor. Karmaşıklık başka bir yerde: karşı tarafın bu itmelerin nerede bittiğini
bilmesi gerekiyor, ve bu bilgiyi taşıyan şey `Write`in kendisi değil, birazdan ölçülecek olan
çerçeveleme.

## Çerçevesiz Akış: Üç İtme Tek Bir Bayt Dizisine Karışıyor

**AG35.** `cercevesizBaglanti`ya yazılan üç mesaj (`"merhaba"`, `"veri"`, `"kapanis"`)
aralarında hiçbir ayraç olmadan doğrudan art arda ekleniyor — sonuç, on sekiz baytlık tek
bir dizi. Okuyan taraf bu dizide bir mesajın nerede bitip ötekinin nerede başladığını **hiç
bilmiyor**; bildiği tek şey sabit boyutlu (altı baytlık) parçalar okumak. Bu parçalardan
hiçbiri özgün üç mesajdan biriyle eşleşmiyor — `"merhab"`, `"averik"`, `"apanis"` — çünkü
altı bayt sınırı mesaj sınırlarıyla hiçbir ilişkisi olmayan, tamamen keyfi bir bölme. Ölçüm
bunu `0/3` olarak veriyor: sabit boyutlu okuma, mesaj sınırlarını korumanın **hiçbir**
garantisini taşımıyor.

Altı bayt seçimi ölçümün bir ayrıntısı değil, tam olarak bu keyfiliği göstermek için
seçildi: üç mesajın uzunlukları (7, 4, 7) hiçbiri altıya bölünmüyor, bu yüzden hiçbir okuma
bir mesajın tam sınırında durmuyor. Okuma boyutu dört olsaydı ya da sekiz olsaydı sonuç yine
`0/3` olurdu, çünkü sorun okuma boyutunun büyüklüğü değil, okuma boyutunun mesaj
uzunluklarından **bağımsız**, dışarıdan seçilen bir sayı olması. Bu, uzun süreli bir
bağlantının temel bir özelliğine işaret ediyor: `io.Reader`ın bir `Read` çağrısı, kaç bayt
istendiyse o kadarını (ya da o an elde ne kadar varsa) döndürüyor — hangi baytların bir
mesajın başı, hangilerinin ortası olduğuna dair hiçbir bilgi taşımıyor. Bu bilgi akışın
kendisinde yoksa, okuyan tarafın onu **icat etmesi** gerekir; sabit boyutlu okuma böyle bir
icat, ve bu ölçüm onun neden işe yaramadığını gösteriyor.

## Çerçeveli Akış: Sınır Bilgisiyle Geri Ayrışıyor

**AG36.** `cerceveliBaglanti`ya yazılan her mesajdan önce, o mesajın uzunluğunu taşıyan
dört baytlık bir önek duruyor. Okuyan taraf artık tahmin etmiyor: önce dört baytı okuyup
uzunluğu öğreniyor, sonra tam o uzunlukta bir okuma yapıyor — ne eksik, ne fazla. Üç mesajın
üçü de bayt bayt doğru geri geliyor, `3/3`. Aradaki fark okuma boyutunun büyüklüğü değil
(çerçevesiz denemede altı bayt, mesajlardan bazılarından uzun bazılarından kısaydı); fark,
okuma boyutunun **her seferinde mesajın kendisinden geldiği**, dışarıdan sabit bir sayı
olarak dayatılmadığı.

`cerceveOku` iki okumasında da `io.ReadFull`ı kullanıyor, düz bir `Read` çağrısını değil.
Bu seçim rastgele değil: `io.Reader` arayüzünün sözleşmesi, istenen bayt sayısından **daha
az** döndürmeye izin veriyor, elde daha fazla veri olsa bile — `bytes.Buffer` bu ölçümde
her zaman istenen kadarını verdi, ama bu, arayüzün genel sözleşmesinin bir parçası değil,
bu belirli somut tipin bir davranışı. `io.ReadFull`, istenen bayt sayısı tam olarak
karşılanana kadar (ya akış bitene ya da bir hata çıkana kadar) okumayı tekrarlıyor; dört
baytlık uzunluk önekinin ya da değişken uzunluklu gövdenin tek bir `Read` çağrısında parça
parça, eksik gelmesi ihtimaline karşı bu bir güvence. Çerçeveleme kuralının kendisi kadar,
onu okuyan kodun bu ayrıntıya dikkat etmesi de doğru ayrışmanın bir parçası.

Uzunluk öneki, çerçevelemenin tek olası biçimi değil — bir mesajın sonunu işaretleyen özel
bir bayt (bir ayraç) da aynı işi görebilirdi. Ama ayraç tabanlı bir çerçeveleme, mesajın
içeriğinde ayracın kendisiyle aynı baytın geçme ihtimalini taşır: `"veri"` mesajı `"i"`
harfiyle bitiyor, ayraç olarak `"i"` seçilmiş olsaydı bu mesaj kendi içinde erken kesilmiş
gibi görünürdü. Uzunluk öneki bu riski taşımıyor, çünkü mesajın içeriğine hiç bakmıyor —
önce "kaç bayt geleceğini" söylüyor, sonra o kadar baytı, içinde ne olursa olsun, tek bir
birim olarak okuyor. Bu ders bu iki stratejiyi karşılaştırmıyor, yalnızca ikincisini
ölçüyor, ama tercihin gerekçesi bu.

Kursun on üç sınır geçişinin tam dağılımı (1 korunuyor, 5 yeniden kuruluyor, 7 kayboluyor;
03. derste kurulan sayı) bu on üç geçişin dışında kalan, ayrı bir alt problemi çözüyor:
o dağılım tek bir değerin sınırı geçerken tipine ne olduğunu sayıyordu, bu ders ise bir
akışın **neresinin** bir değer olduğunu belirliyor. Çerçeveleme bu sorunun cevabı olmadan,
önceki derslerin JSON çözücüsü de tarif üretici de çalışamazdı — ikisi de baştan "burada bir
mesaj bitti" bilgisine ihtiyaç duyuyor, ve bu bilgiyi taşıyan tek şey çerçeveleme.

Bilgisayar Ağları müfredatının Uygulama Katmanı Protokolleri kursu ile Frontend Geliştirme
müfredatının Uygulama Mimarisi: Yönlendirme, Durum ve Veri kursu, uzun süreli bağlantılar
ve gerçek zamanlı veri akışını kavram olarak zaten kurdu; burada tekrarlanmıyor, ölçülen
yalnızca Go'nun standart kitaplığının bir bayt akışını arayüz düzeyinde nasıl taşıdığı.

**AG37.** Son ölçüm bir uyarı: `cerceveleYaz` ile yazılıp `cerceveOku` ile geri gelen
gövde, `"42"` dizgisi — çerçeveleme burada da kusursuz çalışıyor, gövde bayt bayt doğru.
Ama `govde`nin Go tipi `string`, `int` değil. Çerçeveleme mesajın **nerede bittiğini**
söylüyor, içindeki baytların bir tamsayıyı mı, bir dizgiyi mi, yoksa bir yapıyı mı temsil
ettiğini söylemiyor. Bu soruyu yanıtlamak, önceki dersin ölçtüğü türden bir tarif
gerektiriyor — çerçeveleme ile tarif, uzun süreli bir bağlantıda birbirini tamamlayan iki
ayrı katman: biri sınırı çiziyor, öbürü sınırın içindekini adlandırıyor.

Bu ayrımın önemi, `"42"` gibi bir gövdenin **iki türlü de** okunabilir olmasında ortaya
çıkıyor. Karşı taraf bu gövdeyi bir tamsayıya çevirmeye çalışırsa (`strconv.Atoi` ile)
başarılı olur ve `42` sayısını elde eder; hiç çevirmeden dizgi olarak tutarsa yine geçerli
bir değeri olur. Çerçeveleme her iki yorumu da **eşit derecede mümkün** bırakıyor, çünkü
gövdenin baytları kendi başına hangi yorumun doğru olduğunu söylemiyor. Önceki derste
görülen sözleşme temelli tarif tam olarak bu belirsizliği kapatan şeydi: alan adı ve tipini
önceden sabitleyerek, gövdenin nasıl okunacağı konusunda iki tarafı da aynı karara
bağlıyordu. Uzun süreli bir bağlantıda bu iki katman genellikle birlikte kuruluyor —
çerçeveleme her mesajın sınırını, tarif her mesajın içindeki alanların anlamını taşıyor —
ama bu ders onları bilinçli olarak ayırdı, çünkü ikisi birbirinden bağımsız başarabilir ya
da başarısız olabilir: bir akış doğru çerçevelenip yine de yanlış yorumlanabilir.

## Özet

- Uzun süreli bir bağlantı, gerçek bir ağ bağlantısı açılmadan, `io.Reader` ve `io.Writer`ı
  gerçekleştiren bir `bytes.Buffer` ile modellendi; ölçülen kod yalnızca bu iki arayüzü
  kullandığı için aynı mantık gerçek bir bağlantı üzerinde de değişmeden çalışırdı.
- Sunucu tarafı üç mesajı aralarında bir istek beklemeden itti — uzun süreli bir bağlantının
  veri itme özelliği, önceki derslerin istek–yanıt kalıbından bu yönüyle ayrılıyor.
- Çerçevesiz bir akışta üç mesaj tek bir bayt dizisine karıştı ve sabit boyutlu okumalar
  hiçbirini doğru ayıramadı (`0/3`); uzunluk ön ekiyle çerçevelenen akışta üçü de bayt bayt
  doğru geri geldi (`3/3`), çünkü okuma boyutu her seferinde mesajın kendisinden geldi.
- `io.ReadFull` kullanımı, `io.Reader` sözleşmesinin istenen bayt sayısından azını
  döndürmeye izin verdiği durumlar için bir güvenceydi — çerçeveleme kuralının doğru
  çalışması, onu okuyan kodun bu ayrıntıyı gözetmesine de bağlıydı.
- Sınırlayıcı ölçüm: çerçeveleme doğru gövdeyi verdi ama gövdenin tipini vermedi — `"42"`
  dizgisi doğru ayrıştı, ama bunun bir tamsayı mı bir dizgi mi olduğu çerçevelemenin dışında
  kalan, ayrı bir tarif sorusu.
- Çerçeveleme ve tarif, uzun süreli bir bağlantıda birbirini tamamlayan iki ayrı katman:
  biri mesajın sınırını, öbürü sınırın içindekinin anlamını taşıyor.

## Sonraki Adım

Bu ders bir akışın neresinin bir mesaj olduğunu belirleyen tek bilgiyi — çerçevelemeyi —
ölçtü, ve bu bilginin bir mesajın **tipini** hiç taşımadığını gösterdi: aynı çerçeve içinde
ne olduğu yine bir tarif gerektiriyor. Kursun bu konusu (Ağ ve Veri Biçimleri) altı dersi
boyunca sınırı hep ağ üzerinden — bir istek, bir yanıt, bir şablon çıktısı, iki sürecin
paylaştığı bir tarif, uzun süreli bir bağlantı — geçen değerleri ölçtü. Sıradaki konu aynı
soruyu programın kendi sınırlarında soruyor: dosya, arabellek ve gömülü bir kaynak da,
tıpkı bu dersteki bağlantı gibi, bayt düzeyinde konuşuyor — ve orada da tip bilgisinin
sınırın hangi tarafında durduğu, hangi tarafta durmadığı yeniden ölçülecek.
