Ana içeriğe geç

Engine.IO protokolü

Bu belge Engine.IO protokolünün 4. sürümünü tanımlamaktadır.

Bu belgenin kaynağı burada bulunabilir.

İçindekiler

  • Giriş
  • Taşımacılıklar
    • HTTP uzun-polling
      • İstek yolu
      • Sorgu parametreleri
      • Başlıklar
      • Veri gönderme ve alma
        • Veri gönderme
        • Veri alma
    • WebSocket
  • Protokol
    • El sıkışma
    • Kalp atışı
    • Yükseltme
    • Mesaj
  • Paket kodlama
    • HTTP uzun-polling
    • WebSocket
  • Tarihçe
    • v2'den v3'e
    • v3'ten v4'e
  • Test paketi

Giriş

Engine.IO protokolü, bir istemci ile bir sunucu arasında full-duplex ve düşük maliyetli iletişim sağlar.

Bu protokol, WebSocket protokolü üzerine kuruludur ve WebSocket bağlantısı kurulamazsa, geri dönüş olarak HTTP uzun-polling kullanır.

Referans uygulaması TypeScript ile yazılmıştır:

Socket.IO protokolü, Engine.IO protokolü tarafından sağlanan iletişim kanalı üzerinde ek özellikler getirerek bu temeller üzerine inşa edilmiştir.

Taşımacılıklar

Engine.IO istemcisi ile Engine.IO sunucusu arasındaki bağlantı şunlarla kurulabilir:

  • HTTP uzun-polling
  • WebSocket

HTTP uzun-polling

HTTP uzun-polling taşımacılığı (kısaca "polling" olarak da anılır) ardışık HTTP isteğinden oluşur:

  • sunucudan veri almak için uzun süreli GET istekleri
  • sunucuya veri göndermek için kısa süreli POST istekleri

İstek yolu

HTTP isteklerinin yolu varsayılan olarak /engine.io/'dır.

Bu yol, protokol üzerine inşa edilmiş kütüphaneler tarafından güncellenebilir (örneğin, Socket.IO protokolü /socket.io/ kullanır).

Sorgu parametreleri

Aşağıdaki sorgu parametreleri kullanılır:

İsimDeğerAçıklama
EIO4Zorunlu, protokolün sürümü.
transportpollingZorunlu, taşımacılığın adı.
sid``Zorunlu, oturum kurulduktan sonra oturum kimliği.

Eğer zorunlu bir sorgu parametresi eksikse, sunucu HTTP 400 hata durumu ile yanıt vermelidir.

Başlıklar

İkili veri gönderirken, gönderen (istemci veya sunucu) Content-Type: application/octet-stream başlığını eklemelidir.

Açık bir Content-Type başlığı olmadan, alıcı verinin düz metin olduğunu varsaymalıdır.

Referans: https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Type

Veri gönderme ve alma

Veri gönderme

Paket göndermek için, bir istemci HTTP POST isteği oluşturmalıdır ve paketler isteğin gövdesinde kodlanmalıdır:

CLIENT                                                 SERVER

│ │
│ POST /engine.io/?EIO=4&transport=polling&sid=... │
│ ───────────────────────────────────────────────────► │
│ ◄──────────────────────────────────────────────────┘ │
│ HTTP 200 │
│ │

Eğer oturum kimliği (sid sorgu parametresinden) bilinmiyorsa, sunucu HTTP 400 yanıtı döndürmelidir.

Başarıyı belirtmek için sunucu, yanıt gövdesinde ok dizesi ile birlikte HTTP 200 yanıtı döndürmelidir.

Paket sıralamasını sağlamak için, bir istemci birden fazla aktif POST isteğine sahip olmamalıdır. Eğer olursa, sunucu HTTP 400 hata durumu döndürmeli ve oturumu kapatmalıdır.

Veri alma

Paket almak için, bir istemci HTTP GET isteği oluşturmalıdır:

CLIENT                                                SERVER

│ GET /engine.io/?EIO=4&transport=polling&sid=... │
│ ──────────────────────────────────────────────────► │
│ . │
│ . │
│ . │
│ . │
│ ◄─────────────────────────────────────────────────┘ │
│ HTTP 200 │

Eğer oturum kimliği ( sid sorgu parametresinden) bilinmiyorsa, sunucu HTTP 400 yanıtı döndürmelidir.

Sunucu, belirtilen oturum için tamponlanmış paket bulunmuyorsa hemen yanıt vermeyebilir. Gönderilecek paketler olduğunda, sunucu bunları kodlamalıdır (bakınız Paket kodlama) ve HTTP isteğinin yanıt gövdesinde göndermelidir.

Paket sıralamasını sağlamak için, bir istemci birden fazla aktif GET isteğine sahip olmamalıdır. Eğer olursa, sunucu HTTP 400 hata durumu döndürmeli ve oturumu kapatmalıdır.

WebSocket

WebSocket taşımacılığı, sunucu ile istemci arasında çift yönlü ve düşük gecikmeli bir iletişim kanalı sağlayan bir WebSocket bağlantısı oluşturur.

Aşağıdaki sorgu parametreleri kullanılır:

İsimDeğerAçıklama
EIO4Zorunlu, protokolün sürümü.
transportwebsocketZorunlu, taşımacılığın adı.
sid``İsteğe bağlı, HTTP uzun-polling'den yükseltmenin olup olmadığına bağlı.

Eğer zorunlu bir sorgu parametresi eksikse, sunucu WebSocket bağlantısını kapatmalıdır.

Her paket (okuyun veya yazın) kendi WebSocket çerçevesinde gönderilir.

Bir istemci bir oturumda birden fazla WebSocket bağlantısı açmamalıdır. Eğer olursa, sunucu WebSocket bağlantısını kapatmalıdır.

Protokol

Bir Engine.IO paketi şunlardan oluşur:

  • bir paket türü
  • isteğe bağlı paket yükü

Mevcut paket türlerinin listesi:

TürIDKullanım
açık0el sıkışma sırasında kullanılır.
kapalı1Bir taşımacılığın kapatılabileceğini belirtmek için kullanılır.
ping2kalp atışı mekanizmasında kullanılır.
pong3kalp atışı mekanizmasında kullanılır.
mesaj4Diğer tarafa bir yük göndermek için kullanılır.
yükselt5yükseltme işlemi sırasında kullanılır.
noop6yükseltme işlemi sırasında kullanılır.

El sıkışma

Bir bağlantı kurmak için istemci sunucuya HTTP GET isteği göndermelidir:

  • Varsayılan olarak önce HTTP uzun-polling
CLIENT                                                    SERVER

│ │
│ GET /engine.io/?EIO=4&transport=polling │
│ ───────────────────────────────────────────────────────► │
│ ◄──────────────────────────────────────────────────────┘ │
│ HTTP 200 │
│ │
  • Sadece WebSocket oturumu
CLIENT                                                    SERVER

│ │
│ GET /engine.io/?EIO=4&transport=websocket │
│ ───────────────────────────────────────────────────────► │
│ ◄──────────────────────────────────────────────────────┘ │
│ HTTP 101 │
│ │

Sunucu bağlantıyı kabul ederse, open paketi ile birlikte aşağıdaki JSON biçiminde kodlanmış yükle yanıt vermelidir:

AnahtarTürAçıklama
sidstringOturum kimliği.
upgradesstring[]Mevcut taşımacılık yükseltmeleri listesidir.
pingIntervalnumberkalp atışı mekanizmasında kullanılan ping aralığı (milisaniye cinsinden).
pingTimeoutnumberkalp atışı mekanizmasında kullanılan ping zaman aşımı (milisaniye cinsinden).
maxPayloadnumberyükleri bir araya getirmek için istemci tarafından kullanılan, her parça için maksimum bayt sayısı.

Örnek:

{
"sid": "lv_VI97HAXpY6yYWAAAC",
"upgrades": ["websocket"],
"pingInterval": 25000,
"pingTimeout": 20000,
"maxPayload": 1000000
}

İstemci, sonraki tüm isteklerin sorgu parametrelerinde sid değerini göndermelidir.

Kalp atışı

El sıkışma tamamlandıktan sonra, bağlantının canlılığını kontrol etmek için bir kalp atışı mekanizması başlatılır:

CLIENT                                                 SERVER

│ *** El Sıkışma *** │
│ │
│ ◄───────────────────────────────────────────────── │
│ 2 │ (ping paketi)
│ ─────────────────────────────────────────────────► │
│ 3 │ (pong paketi)

Belirli bir aralıkta (el sıkışmada gönderilen pingInterval değeri) sunucu bir ping paketi gönderir ve istemcinin birkaç saniye (ping zaman aşımı değeri) içinde geri bir pong paketi göndermesi gerekir.

Eğer sunucu geri bir pong paketi almazsa, bağlantının kapandığını kabul etmelidir.

Tersine, eğer istemci pingInterval + pingTimeout içinde bir ping paketi almazsa, o zaman bağlantının kapandığını kabul etmelidir.

Yükseltme

Varsayılan olarak, istemci önce HTTP uzun-polling bağlantısı oluşturmalı, ardından kullanılabilir ise daha iyi taşımacılıklara yükseltmelidir.

WebSocket'a yükseltmek için, istemci şunları yapmalıdır:

  • HTTP uzun-polling taşımacılığını duraklatmak (artık HTTP isteği gönderilmeyecek), böylece hiçbir paket kaybolmasın
  • Aynı oturum kimliği ile bir WebSocket bağlantısı açmak
  • Yükde probe dizesi ile bir ping paketi göndermek

Sunucu şunları yapmalıdır:

  • HTTP uzun-polling taşımacılığını düzgün bir şekilde kapatmak için herhangi bir bekleyen GET isteğine bir noop paketi göndermek (eğer uygunsa)
  • Yükde probe dizesi ile bir pong paketi ile yanıt vermek

Son olarak, istemci yükseltmeyi tamamlamak için bir upgrade paketi göndermelidir:

CLIENT                                                 SERVER

│ │
│ GET /engine.io/?EIO=4&transport=websocket&sid=... │
│ ───────────────────────────────────────────────────► │
│ ◄─────────────────────────────────────────────────┘ │
│ HTTP 101 (WebSocket el sıkışma) │
│ │
│ ----- WebSocket çerçeveleri ----- │
│ ─────────────────────────────────────────────────► │
│ 2probe │ (ping paketi)
│ ◄───────────────────────────────────────────────── │
│ 3probe │ (pong paketi)
│ ─────────────────────────────────────────────────► │
│ 5 │ (yükseltme paketi)
│ │

Mesaj

El sıkışma tamamlandıktan sonra, istemci ve sunucu, verileri message paketinde dahil ederek değiştirebilir.

Paket kodlama

Bir Engine.IO paketinin serileştirilmesi yükün (düz metin veya ikili) türüne ve taşımacılığa bağlıdır.

HTTP uzun-polling

HTTP uzun-polling taşımacılığının doğası gereği, birden fazla paket, verimliliği arttırmak için tek bir yükte birleştirilebilir.

Format:

<paket türü>[<veri>]<separator><paket türü>[<veri>]<separator><paket türü>[<veri>][...]

Örnek:

4hello\x1e2\x1e4world

şu ile:

4 => mesaj paket türü
hello => mesaj yükü
\x1e => ayırıcı
2 => ping paket türü
\x1e => ayırıcı
4 => mesaj paket türü
world => mesaj yükü

Paketler kayıt ayrıcı karakter: \x1e ile ayrılır.

İkili yükler base64 olarak kodlanmalı ve bir b karakteriyle ön eklenmelidir:

Örnek:

4hello\x1ebAQIDBA==

şu ile:

4 => mesaj paket türü
hello => mesaj yükü
\x1e => ayırıcı
b => ikili ön ek
AQIDBA== => <01 02 03 04> olarak kodlanmış tampon

İstemci, el sıkışma sırasında gönderilen maxPayload değerini kullanarak kaç paketin birleştirileceğini belirlemelidir.

WebSocket

Her Engine.IO paketi kendi WebSocket çerçevesinde gönderilir.

Format:

<paket türü>[<veri>]

Örnek:

4hello

şu ile:

4 => mesaj paket türü
hello => mesaj yükü (UTF-8 olarak kodlanmış)

İkili yükler, değişiklik olmaksızın olduğu gibi gönderilir.

Tarihçe

v2'den v3'e

  • ikili veri desteği ekleme

Protokolün 2. sürümü Socket.IO v0.9 ve alt sürümlerinde kullanılır.

Protokolün 3. sürümü Socket.IO v1 ve v2 sürümlerinde kullanılır.

v3'ten v4'e

  • ters ping/pong mekanizması

Ping paketleri şimdi sunucu tarafından gönderilmektedir, çünkü tarayıcılardaki zamanlayıcılar yeterince güvenilir değildir. Birçok zaman aşımı sorununun istemci tarafında zamanlayıcıların gecikmesinden kaynaklandığını düşünüyoruz.

  • ikili veri ile yük kodlarken her zaman base64 kullanımı

Bu değişiklik, tüm yükleri (ikili olup olmamalarına bakılmaksızın) aynı şekilde ele almayı sağlar, istemci veya mevcut taşımacılığın ikili veriyi destekleyip desteklemeyeceğini dikkate almadan.

Lütfen bunun yalnızca HTTP uzun-polling için geçerli olduğunu unutmayın. İkili veriler, WebSocket çerçevelerinde ek bir dönüşüm olmaksızın gönderilir.

  • karakter sayma yerine bir kayıt ayırıcı (\x1e) kullanımı

Karakter sayma, diğer dillerde protokolü uygulamayı zorlaştırdı (veya en azından zorlaştırdı), bu diller UTF-16 kodlaması kullanmayabilir.

Örneğin, 2:4€ olarak kodlanırken, Buffer.byteLength('€') === 3 olur.

Not: bu, kayıt ayrıcısının verilerde kullanılmadığını varsayıyor.

Protokolün 4. sürümü (mevcut) Socket.IO v3 ve üzeri sürümlerde bulunmaktadır.

Test paketi

test-suite/ dizinindeki test paketi, bir sunucu uygulamasının uyumluluğunu kontrol etmenizi sağlar.

Kullanım:

  • Node.js'te: npm ci && npm test
  • Tarayıcıda: sadece index.html dosyasını tarayıcınızda açmanız yeterlidir.

Referans için, tüm testleri geçmek için JavaScript sunucusu için beklenen yapılandırma:

import { listen } from "engine.io";

const server = listen(3000, {
pingInterval: 300,
pingTimeout: 200,
maxPayload: 1e6,
cors: {
origin: "*"
}
});

server.on("connection", socket => {
socket.on("data", (...args) => {
socket.send(...args);
});
});