Yeniden bağlanma ve limitler
Bu sayfa, realtime soketi koptuğunda durumun nasıl kurtarıldığını, ResyncRequired geldiğinde ne yapılacağını, boşta kapanma (4100) davranışını ve sunucunun uyguladığı limitleri anlatır. Mobil ya da dengesiz ağda oynanan bir oyun yazıyorsanız bu sayfayı baştan sona okuyun.
Kopma sonrası kurtarma
Soket her zaman kopabilir (ağ, dağıtım, yük dengeleyici). Open Play’de kurtarma yeni soketle yeniden katılma (re-join) ile yapılır; eski soketin durumu sunucuda yeniden kullanılmaz.
- Yeni bilet al. Her bağlantı yeni bir bilet ister (tek kullanımlık, 30 sn); eski bilet yeniden kullanılamaz (protokol).
Authenticate,Ready. YenisessionEpochgelir. Yeni soket aynı kaynağa yazmaya başladığında oyuncunun eski soketi artık yazar değildir: eski soketin girdileriAck: Stalealır.- Her abonelik için yeniden
Join.- Kalıcı (durable) akışlarda: son aldığınız
cursor’ıJoin.cursorolarak verin. Sunucu kaldığınız yerdenDurableEvent’leri tekrar oynatır ve yeni birDurableRecoverybariyeri yollar (snapshotboştur). - Geçici (ephemeral) durumda:
cursoryoktur;Jointaze birSnapshotgetirir. Gönderilmiş amaAckalmamışInput’lar tekrar oynatılmaz: sonucu belirsizdir, oyun mantığınız en sonSnapshot’a göre devam etmelidir. - Kalıcı komutlarda (
Command):Ackgelmeden kopan komutun sonucu belirsizdir. AynıidempotencyKeyile yeniden gönderin ya da sonucu sorgulayın (eklentileri çağırma); yeni anahtar üretmeyin.
- Kalıcı (durable) akışlarda: son aldığınız
ResyncRequiredgelirse (imleç yok, süresi dolmuş, başka oyuncuya ait, boşluklu ya da tekrar bütçesini aşmış) imleci atın vecursorolmadan tekrarJoinedin; bu taze bir anlık görüntü getirir. Aynı imleçle ısrar etmeyin.
İmleç (cursor) kuralları
- Opaktır ve oyuncuya bağlıdır; başka oyuncunun imleci kullanılamaz. İçeriğini ayrıştırmayın.
- Her
DurableRecoveryve herDurableEventyeni bir imleç taşır; en sonuncusunu saklayın. - Tek kurtarmada en çok 256 kalıcı olay tekrar oynatılır; fazlası
ResyncRequiredile taze anlık görüntüye çevrilir. - İmleç ve teslimat saklama penceresi 24 saattir; ondan eski imleç
ResyncRequiredalır. coveredEventIds’deki olaylar taze anlık görüntüde zaten vardır; bunları ayrıca uygulamayın.
TypeScript SDK ne yapar
SDK bu akışı sizin yerinize yürütür (RealtimeClient, client.realtime):
| Davranış | Varsayılan | Ayar |
|---|---|---|
| Otomatik yeniden bağlanma | açık | autoReconnect |
| Deneme gecikmesi | min(reconnectBaseMs · 2^(n−1), 30 000) ms + [0, reconnectBaseMs) jitter | reconnectBaseMs: 1000 (100 ile 10 000 arası) |
| En çok deneme | 8 | maxReconnectAttempts: 0 ile 20 arası |
| Kimlik doğrulama zaman aşımı | 5 sn | authTimeoutMs (en çok 5 sn) |
Uygulama Ping aralığı | 15 sn | heartbeatMs: 10 ms ile 60 sn |
| Kalıcı komut zaman aşımı | 15 sn | commandTimeoutMs: 1 ms ile 120 sn |
| Cevap bekleyen girdi penceresi | 4 | maxPendingInputs: 1 ile 8 |
| Çerçeve boyutu | 16 KiB | maxFrameBytes (en çok 16 KiB) |
Durum makinesi idle, ticketing, connecting, authenticating, ready, recovering, live, backoff, reauthRequired, upgradeRequired, idleClosed ve closed değerlerinden geçer. Önemli olanlar:
recovering/backoff: SDK yeniden bağlanıyor; aboneliklerin imleçleri korunur.reauthRequired: bilet ya da kimlik reddedildi (AuthenticationFailed, 401/403). Oturumu yeniden kurun; SDK kendiliğinden sonsuz döngüye girmez.upgradeRequired: sunucu protokol sürümünü ya da çerçeveyi tanımadı (UnsupportedProtocol, 426). İstemciyi güncelleyin.- Sunucu
PayloadTooLargeya daCapacityExceededgönderirse SDK bunu geri basınç (backpressure) hatası olarak yüzeye çıkarır; bağlantı durumunu değiştirmez.
Not
Tamamlanmamış girdiler soket koptuğunda “belirsiz sonuçlu” olarak reddedilir ve yeniden bağlanınca otomatik tekrar gönderilmez. Sinyal (
best-effort) mesajları da tekrar edilmez.
Boşta kapanma (4100)
Sunucu, girdi kabul eden (etkileşimli) bir aboneliği olan ve belirli süre oyuncu etkinliği göndermeyen soketi kapatır.
- Kapanış kodu
4100, nedenidle_timeout. TipliErrorçerçevesi yoktur; yalnız WebSocket kapanış çerçevesi. - Süre varsayılan 5 dakikadır ve operatör tarafından değiştirilebilir ya da kapatılabilir. Kontrol aralığı
min(süre/4, 15 sn)olduğundan kapanış eşikten en çok bu kadar geç gelir. - Menüde ya da oyun öncesinde (girdi kabul eden abonelik yokken) sayaç işlemez.
- Etkinlik sayılan şey eklentinin tanımıdır: girdi çerçevesi genellikle sayılır; uygulama
Ping,Authenticateve protokol düzeyi ping sayılmaz. Oyununuz duraklatılmış ekranda da bağlı kalacaksa eklentinin tanımladığı canlı tutma girdisini gönderin; duraklatma, garaj gibi ekranlarda canlı tutma girdisi göndermek AFK sayılmamasını sağlar. - Açık bir soket oturumu yaşatmaz: oturum ömrü ancak oyuncu REST isteği yaptıkça uzar (güvenlik); sunucu soketteki oturumu 25 saniyede bir yeniden doğrular.
- Bu kapanışı bir hata olarak ele almayın: oyuncuya “Devam et” gösterin, onayında yeni bilet alıp yeniden bağlanın; imleçler sayesinde abonelikler kurtarılır.
SDK’da onIdleClose geri çağrısını verirseniz istemci idleClosed durumuna geçer ve kendiliğinden yeniden bağlanmaz; kullanıcı onayında connect() çağırın. Geri çağrıyı vermezseniz kapanış sıradan bir kopma gibi işlenir ve SDK kendisi yeniden bağlanır.
import { createPlayerClient, type PlayerClient } from "@ferdiunal/open-play-ts";
/**
* Sunucu boşta kapanışında (`4100`, `idle_timeout`) SDK kendiliğinden yeniden bağlanmaz; oyuncuya "Devam et"
* gösterin ve onayında `connect()` çağırın. Abonelikler imleçleriyle kurtarılır.
*/
export function createClientWithIdlePrompt(
baseUrl: string,
environmentId: string,
showContinueButton: (onContinue: () => void) => void,
): PlayerClient {
const client: PlayerClient = createPlayerClient({
baseUrl,
environmentId,
realtime: {
reconnectBaseMs: 250, // ilk yeniden bağlanma denemesi 250 ms (varsayılan 1000)
onIdleClose: () => showContinueButton(() => void client.realtime.connect()),
},
});
return client;
}
Limitler
| Limit | Değer | Aşılınca |
|---|---|---|
| Bilet ömrü | 30 sn, tek kullanım, Origin’e bağlı | AuthenticationFailed |
Authenticate bekleme süresi | 5 sn | AuthenticationFailed, soket kapanır |
| Kimlik doğrulama fazı çerçevesi | 4 KiB | PayloadTooLarge, kapanır |
| Çerçeve (kimlik doğrulama sonrası) | 16 KiB | PayloadTooLarge, kapanır |
| Taşıma katmanı mesajı | 64 KiB | 16 KiB ile 64 KiB arası tipli hata alır; 64 KiB üstü tipli hata olmadan soket hatasıyla düşer |
Input yükü | en çok 4 KiB ve modülün maxBytes değeri | InvalidPayload |
Command yükü | en çok 16 KiB | InvalidPayload |
| Bağlantı başına çerçeve hızı | 40 çerçeve/sn ortalama, 4 sn’lik (160 çerçeve) patlama payı | CapacityExceeded (retryable: true) ve soket kapanır |
| Oyuncu başına açık soket | 4 | CapacityExceeded (retryable: false), soket kapanır |
| Adres başına açık soket | 8 | CapacityExceeded (retryable: false), soket kapanır (operatör gözlem kipine alırsa yalnız sayılır) |
| Adres başına yeni bağlantı | 30/dakika | yükseltme reddedilir |
| Soket başına abonelik | 32 (sunucu varsayılanı) | Join reddedilir |
| Oturum yeniden doğrulama | 25 sn’de bir | oturum iptal edilmişse AuthenticationFailed ve kapanış |
| Modül kapısı yeniden kontrol | 2 sn’de bir | 1008 / 1013 ile kapanış |
| Kurtarmada tekrar oynatılan olay | 256 | ResyncRequired |
| İmleç saklama | 24 saat | ResyncRequired |
| Boşta kapanma | 5 dk (varsayılan) | 4100 idle_timeout |
Notlar:
- Çerçeve hızı bütçesi
Input’la sınırlı değildir; alınan her metin çerçevesi ve protokol düzeyiPingbir çerçeve sayılır (Pongsayılmaz). Dürüst bir istemci yaklaşık 30 ile 35 çerçeve/sn gönderir; 40/sn’nin altında kalın ve bir duraksamadan sonra biriken çerçeveleri toplu göndermeyin. - Sayılar sunucu varsayılanlarıdır; operatör bazılarını (boşta süresi, bağlantı sınırları, çerçeve bütçesi) ortam yapılandırmasıyla değiştirebilir. Barındırılan bir Open Play kullanıyorsanız değerleri sağlayıcınıza sorun.
Inputhızı ayrıca modülün şema belgesindekiratePerSecondveburstdeğerleriyle tanımlıdır.
Sıkıştırma
- Sunucu permessage-deflate (RFC 7692) sıkıştırmasını destekler, yalnız istemci teklif ederse devreye girer. Tarayıcılar bunu kendiliğinden teklif eder; ayrı bir şey yapmanız gerekmez. Benzer JSON çerçevelerinde belirgin küçülme sağlar.
- Operatör sıkıştırmayı kapatabilir; kapalıysa soket sıkıştırmasız çalışır, protokol değişmez.
- Unity yerel (native) WebSocket istemcisinde sıkıştırma henüz yoktur; bu bilinen bir kısıttır. WebGL’de tarayıcı bunu üstlenir (Unity SDK).
Sunucu tarafı canlılık sondası
Sunucu, bağlantı gecikmesini ölçmek için yaklaşık 5 saniyede bir protokol düzeyi WebSocket Ping gönderir. Tarayıcı ve standart istemci kütüphaneleri Pong’u kendiliğinden yanıtlar; elle WebSocket yazıyorsanız Pong yanıtını unutmayın. Bu sonda oyuncu etkinliği sayılmaz.
Sorun giderme
| Belirti | Olası neden | Çözüm |
|---|---|---|
Her bağlanışta hemen AuthenticationFailed | Bilet tekrar kullanıldı ya da 30 sn geçti; Origin bilet isteğinden farklı | Her bağlantıda yeni bilet alın; bileti aldığınız origin’den bağlanın |
CapacityExceeded ile kapanma | Çerçeve hızı ya da bağlantı sınırı | Çerçeve gönderim hızını düşürün; eski sekmeleri kapatın (oyuncu başına 4 soket) |
ResyncRequired döngüsü | Eski imleçle ısrar | İmleci atın, cursor olmadan Join edin |
| 5 dakika sonra kopma | Boşta kapanma (4100) | “Devam et” akışı kurun ya da canlı tutma girdisi gönderin |
Yükseltme 401 | Sorgu dizesi var, alt protokol istenmedi ya da Origin izinli değil | Protokol: soketi aç |
Daha fazlası için sorun giderme.