İçeriğe geç

Özel Protokol Çözümleme

Yerleşik biçimlerin tanıyamadığı özel veya kurum içi bir soket protokolü mü yakaladınız, elinizde bir yığın byte’tan başka bir şey mi kalmadı? Özel çözümleme, kısa bir betikle aracı onu nasıl okuyacağı konusunda eğitmenizi sağlar: sürekli bir byte akışını tek tek mesajlara bölün, protokol başlığını ayıklayın, gerektiğinde sıkıştırmayı açın ve gerisini aracın otomatik yapılandırılmış tanımasına bırakın.

Bu sayfa, kendi çözümleyicisini yazmak isteyenler içindir. Fonksiyonun nasıl yazılacağını, ne zaman çağrıldığını ve dönüş değerinin ne anlama geldiğini açıklar.


1. Ne olduğu: kare başına bir çözümleme kancası

Bölüm başlığı “1. Ne olduğu: kare başına bir çözümleme kancası”

Bir çözümleme kancası yazarsınız ve araç, bir bağlantının byte’larını ona besleyip tekrar tekrar aynı soruyu sorar: “Buradan başlayarak, tam bir mesaj kesip çıkarabilir misin?” Bir mesaj kesip çıkarırsınız ve kaç byte kullandığınızı bildirirsiniz; araç kalanla tekrar sorar, akış tamamen dilimlenene kadar bu böyle sürer.

Bir ağ çatısında birikimli bir byte çözümleyici yazdıysanız, zihinsel model aynıdır: sürekli bir byte akışı verildiğinde, mesaj sınırlarına siz karar verirsiniz. İki önemli kural:

  • Yönle uğraşmazsınız: bir bağlantının gönderme akışı ve alma akışı, kancanızdan ayrı ayrı geçer ve araç, dilimlenen her mesajı yönüyle otomatik olarak etiketler. Betiğiniz yalnızca “nasıl dilimleneceğiyle” ilgilenir.
  • Dilimledikten sonra render işiyle uğraşmazsınız: dışarı ittiğiniz her mesaj yeniden otomatik tanımadan geçer. İçinde protobuf, JSON veya plist varsa, bu daha da ayrıştırılıp yapılandırılmış hale getirilir. Siz yalnızca kareleme, başlık ayıklama ve sıkıştırma açmadan sorumlusunuz.

Çözümleyicinin tamamı tek bir fonksiyondur:

function decode(buf, out) {
// buf: şu anda ayrıştırma bekleyen byte akışı; out: çıktı toplayıcı; dönüş: bu seferde tüketilen byte sayısı
}

Girdilerin, çıktıların ve dönüş değerinin ne olduğu

Bölüm başlığı “Girdilerin, çıktıların ve dönüş değerinin ne olduğu”

buf (girdi): ayrıştırma bekleyen byte akışı

  • Tür: ArrayBuffer. İçeriği, “en son tükettiğiniz yerden mevcut sona kadar” kalan tüm byte’lardır.
  • Özellik buf.byteLength: şu anda kaç byte kaldığı (her çağrıyla büyüyebilir; bir mesaj dilimlemek için yeterli olup olmadığına bu şekilde karar verirsiniz).
  • ⚠️ Bu bir ham byte tamponudur, Uint8Array değildir, dolayısıyla byte’lara doğrudan indeksle erişemezsiniz (buf[0] undefined döndürür). Byte okumak için 3. bölümdeki yerleşik yardımcıları (u8 / u16be / sub …) kullanın veya kendiniz bir görünüm sarın: new Uint8Array(buf), new DataView(buf).

out (çıktı): çıktı toplayıcı

  • Tür: düz bir dizi.
  • Metot out.push(mesaj byte'ları): bir mesajı dışarı itin; tek bir çağrıda birden fazlasını itebilirsiniz.
  • “Mesaj byte’ları” şunlardan biri olabilir: yerleşik bir yardımcının (sub(...), gunzip(...) gibi) döndürdüğü bir ArrayBuffer, veya bir string. Diğer türler yok sayılır.

Dönüş değeri: kaç byte tükettiğiniz

  • Tür: tam sayı (number), buf’ın başından kullandığınız byte sayısı.
  • > 0: önden bu kadar byte yediniz ve bir mesaj diliminizi çıkardınız → araç bunları atar ve kalanla sizi tekrar çağırır.
  • 0 / return yok / negatif sayı: “önde henüz tam bir mesaj yok” veya “artık dilimlenecek bir şey yok” anlamına gelir → araç döngüyü durdurur ve kalan byte’ları ham olarak gösterir.
  • Dönüş değeri otomatik olarak tampon uzunluğu içinde sınırlandırılır (taşmaları önlemek için), ancak lütfen gerçekten kullandığınız byte sayısını döndürün.
  • Araç, bir bağlantının gönderme akışı üzerinde bir kez, alma akışı üzerinde bir kez geçiş yapar. Her geçişte, decode döngü içinde çağrılır: her seferinde size kalan tüketilmemiş byte’lar verilir, bir mesaj dilimler ve tüketilen sayıyı döndürürsünüz, siz 0 döndürene kadar bu böyle sürer.
  • Bir mesaj yalnızca tüketimi onaylandığında görünür: ittiğiniz mesaj, ancak > 0 döndürdüğünüzde ortaya çıkar. İtip 0 döndürürseniz, döngü durur ve itme iptal edilir, dolayısıyla bir mesaj itmek her zaman kaç byte aldığını döndürmekle eşleştirilmelidir.
  • Döngü bittikten sonra, tüketilmemiş sondaki byte’lar kaybolmaz; tek bir ham veri parçası olarak gösterilirler (örneğin, tamamlanmamış bir mesajın ikinci yarısı).
  • Her akış (yön) için ayrı, yepyeni bir betik ortamı oluşturulur; gönderme akışı ve alma akışı birbirine asla karışmaz.
  • Çağrılar arasında durumu korumak için (bir sayaç, önceki mesajın türü vb.), değişkenleri decode’un dışında tanımlayın. Bunlar aynı akış içinde çağrılar arasında kalıcıdır, yön değiştiğinde veya yeniden çözümlediğinizde sıfırlanır.
  • Çözümleme, zaten yakalanmış veriler üzerinde çalışır ve tekrar tekrar yeniden çalıştırılabilir: betiği düzenleyin, kaydedin ve aynı bağlantıyı yeni kurallarla yeniden çözümlemek için tekrar tıklayın.
  • Betik bir istisna fırlatırsa veya tek bir çalıştırma zaman aşımına uğrarsa, “bu sefer hiçbir şey tüketilmedi” olarak ele alınır, kalan byte’lar ham veri olarak gösterilir ve yakalama hiç veri kaybı olmadan çalışmaya devam eder.
  • Başka bir deyişle, bozuk bir betiğin yapabileceği en kötü şey çözümleyememektir, sizi ham byte’larla bırakır; bağlantıyı çökertmez. Özgürce düzenleyin.
  • Ve hatalar yutulmaz: istisnalar ve zaman aşımları, sonuçların üstündeki hata ayıklama çıktısı panelinde gösterilir (bkz. 5. bölüm), gönderme veya alma akışından geldiği etiketlenerek. Bunları izleyerek doğrudan düzeltmeye gidin.

3. Yerleşik yardımcılar (betikte doğrudan kullanılabilir)

Bölüm başlığı “3. Yerleşik yardımcılar (betikte doğrudan kullanılabilir)”

Yaygın işler, byte okumak, tam sayı okumak, metne dönüştürmek ve sıkıştırmayı açmak, hepsi yerleşiktir, tekerleği yeniden icat etmenize gerek yok:

Kategori İmza Açıklama
Alt dilim alma sub(buf, off[, len]) off’tan dilimler (sona gitmek için len’i atlayın), ArrayBuffer döndürür
Tam sayı okuma u8(buf, off) / u16be / u16le / u32be / u32le (buf, off) Büyük-endian / küçük-endian işaretsiz tam sayılar okur
Metne dönüştürme hex(buf) / ascii(buf) Onaltılık dizeye dönüştürür / metin olarak okur
Sıkıştırma algılama gzipMagic(buf) gzip olup olmadığı (bir boolean döndürür)
Sıkıştırma açma gunzip / inflate / unzstd / lz4dtx (buf) Sıkıştırmayı açar; açamazsa girdi değişmeden döndürülür, hata vermez
Hata ayıklama log(...args) / console.log(...args) Hata ayıklama paneline yazdırır (bkz. 5. bölüm)

Standart byte okuma/yazma yetenekleri (DataView, Uint8Array vb.) da doğrudan kullanılabilir.


① Uzunluk öneki [4 byte büyük-endian uzunluk][yük], en yaygın özel protokol şekli:

function decode(buf, out) {
if (buf.byteLength < 4) return 0 // uzunluk başlığı tam olarak gelmedi, bekle
const total = 4 + u32be(buf, 0) // tüm mesaj = 4 byte'lık başlık + yük
if (buf.byteLength < total) return 0 // tüm mesaj tam olarak gelmedi, bekle
out.push(sub(buf, 4, total - 4)) // başlığı ayıkla, yükü otomatik tanımaya devret
return total // bu mesajı tüket, bir sonrakini dilimlemeye geç
}

② Ayırıcıya / satıra göre, bir satır sonu gibi bir işaretçide kareleri dilimleme:

function decode(buf, out) {
const i = ascii(buf).indexOf('\n')
if (i < 0) return 0 // henüz satır sonu yok, bekle
out.push(sub(buf, 0, i)) // bu satırı it (satır sonu olmadan)
return i + 1 // satır sonunu da birlikte tüket
}

③ Tamamını bir kerede işleme, tüm akışı tek bir mesaj olarak ele alma, örneğin tamamını sıkıştırmadan çıkarma:

function decode(buf, out) {
out.push(gzipMagic(buf) ? gunzip(buf) : buf) // gzip ise, aç
return buf.byteLength // her şeyi tüket, döngü hemen biter
}

④ Türe göre dağıtma, başlıkta bir tür alanıyla ve türe göre farklı işleme:

function decode(buf, out) {
if (buf.byteLength < 4) return 0
const total = 4 + u32be(buf, 0)
if (buf.byteLength < total) return 0
const type = u8(buf, 4) // ilk byte mesaj türüdür
const body = sub(buf, 5, total - 5)
out.push(type === 2 ? gunzip(body) : body) // tür 2 sıkıştırılmıştır
return total
}

Editörde bu kalıpların tümü için hazır şablonlar vardır; birini ekleyin, birkaç sayıyı değiştirin ve çalıştırın.

Özel çözümleyici editörü: bir decode(buf, out) betiği yazın; “Şablon ekle” uzunluk öneki / sihirli imza / ayırıcı / sabit uzunluk / gzip / türe göre dağıtım için hazır iskeletler sağlar, ayrıca hızlı bir fonksiyon referansı


  • Yaz ve kaydet: çözümleyici editöründe oluşturun, adlandırın ve kaydedin; başlamak için bir şablon ekleyin, yerleşik fonksiyonlar hızlı bir referansta el altında.
  • Uygulama: anlam veremediğiniz bir bağlantı için sağ tıklayıp “Şu şekilde çözümle” → çözümleyiciniz seçin, bağlantının tüm gönderme ve alma trafiği hemen kurallarınıza göre dilimlenip gösterilir.
  • Hata ayıklama ve değişkenleri inceleme: decode içinde, herhangi bir değeri yazdırmak için log(...) veya console.log(...) kullanın: byte sayıları, tür alanları, hex(sub(buf, 0, 8)) ve benzeri. Çıktı, “Şu şekilde çözümle” sonuçlarının üstündekiHata ayıklama çıktısı” panelinde görünür, her satır gönderme veya alma akışından geldiği etiketlenmiş olarak. Betik hataları ve zaman aşımları aynı panelde, yine yönle etiketlenmiş olarak görünür. Bu şekilde yazdırıp konum bulun; kör kör düzenlemekten çok daha hızlıdır.

    Sandbox’ta tam bir tarayıcı / Node console’u yoktur; yalnızca console.log ve eşdeğeri log bu panele bağlıdır. TextDecoder, fetch, setTimeout ve benzerleri kullanılamaz. Byte’ları metin olarak okumak için ascii(buf) kullanın.

  • Düzenle ve canlı gör: yeniden yakalamaya gerek yok. Betiğinizi kaydedin, “Şu şekilde çözümle”ye tekrar tıklayın, aynı bağlantı yeni kurallarla yerinde yeniden çözümlenir. İstediğiniz şekilde dilimleyene kadar yineleyin.
  • Yapıya geçiş: çözümlenen her parça otomatik tanımaya geri verilir, böylece protobuf, JSON ve plist daha da ayrıştırılıp yapılandırılmış hale getirilir; Verileri inceleme ve çözümleme sayfasındaki birden fazla görünüm üzerinden görüntülenebilir.
  • Çözümleyiciler adlandırılıp bir liste olarak tutulabilir; istediğiniz zaman eklenebilir, düzenlenebilir veya silinebilir.

  • Yerleşik biçimlerin tanıyamadığı kurum içi veya özel bir soket protokolü (yaygın bir şekil uzunluk öneki + protobuf / JSON / ikili) yakaladığınızda, dilimlemek ve okunabilir bir yapıya geri döndürmek için bir çözümleyici yazın.
  • Özel bir başlıkla sarılmış veya bir tur sıkıştırmadan geçmiş bir byte yığınını yeniden okunabilir içeriğe dönüştürmek istediğinizde.

Birleştirilmiş bir hızlı referans: çözümleme giriş noktası, yerleşik yardımcı fonksiyonlar ve çalışma zamanı ortamı. buf alan her fonksiyon, bir ArrayBuffer veya bir string kabul eder.

decode(buf, out) → number
Amaç Çözümleyicinin tek giriş noktası, uygulanması zorunludur. buf’ın önünden mesajları dilimleyin, out’a push edin ve bu seferde tüketilen byte sayısını döndürün.
buf ArrayBuffer, ayrıştırma bekleyen kalan byte akışı. Uzunluğu için buf.byteLength kullanın; byte’lara indeksle erişemezsiniz, aşağıdaki yardımcıları veya new DataView(buf) / new Uint8Array(buf)’ı kullanın.
out Array, çıktı toplayıcı. out.push(bytes) bir mesajı dışarı iter (bytes bir ArrayBuffer veya string’dir; diğer türler yok sayılır); tek seferde birden fazlasını itebilirsiniz.
Dönüş number, buf’ın önünden tüketilen byte’lar. > 0 devam eder; 0 / negatif / dönüş yok → durur ve kalan byte’lar ham olarak gösterilir.
Çağrı kuralı Bağlantının gönderme akışı ve alma akışı üzerinde bir geçiş çalıştırın; her geçiş, siz 0 döndürene kadar her seferinde kalan tüketilmemiş byte’ları geçirerek tekrar tekrar çağırır.

Alt dilim alma

İmza Döndürür Açıklama
sub(buf, off) ArrayBuffer off’tan sona dilimler
sub(buf, off, len) ArrayBuffer off’tan len byte dilimler; aralık dışı off / len otomatik olarak sınırlandırılır, hata vermez

Tam sayı okuma (işaretsiz; off aralık dışıysa 0 döndürür)

İmza Döndürür Açıklama
u8(buf, off) number 1 byte okur
u16be(buf, off) / u16le(buf, off) number 2 byte okur, büyük-endian / küçük-endian
u32be(buf, off) / u32le(buf, off) number 4 byte okur, büyük-endian / küçük-endian

Metne dönüştürme

İmza Döndürür Açıklama
hex(buf) string Küçük harfli onaltılığa dönüştürür
ascii(buf) string Metin olarak okur (ASCII / metin içeriği için uygundur)

Sıkıştırma / sıkıştırma açma (açamazsa girdi değişmeden döndürülür; tek seferlik sıkıştırma açma sınırı yaklaşık 16 MB)

İmza Döndürür Açıklama
gzipMagic(buf) boolean gzip sihirli numarasıyla başlayıp başlamadığı
gunzip(buf) ArrayBuffer gzip sıkıştırma açma
inflate(buf) ArrayBuffer zlib / deflate sıkıştırma açma
unzstd(buf) ArrayBuffer zstd sıkıştırma açma
lz4dtx(buf) ArrayBuffer Blok LZ4 akışı sıkıştırma açma

Hata ayıklama

İmza Döndürür Açıklama
log(...args) (yok) Argümanları tek bir satırda birleştirip “Hata ayıklama çıktısı” paneline yazdırır; bir ArrayBuffer onaltılık olarak gösterilir
console.log(...args) (yok) log ile aynı (console.log’a alışkın olanlar için uygun)
  • Standart JavaScript: ArrayBuffer, tipli diziler (Uint8Array vb.), DataView, JSON, Math, RegExp, Date gibi yaygın yerleşik nesnelerin hepsi kullanılabilir; daha alt seviye byte okuma/yazmaya ihtiyacınız olduğunda new DataView(buf) / new Uint8Array(buf) el altındadır.
  • Hata ayıklama: log(...) / console.log(...) “Hata ayıklama çıktısı” paneline yazdırır; betik hataları ve zaman aşımları da orada, gönderme veya alma akışından geldiği etiketlenerek yazdırılır.
  • Sağlanmayan diğer tarayıcı / Node’a özgü yetenekler: log dışındaki console metotları, TextDecoder / TextEncoder, fetch, setTimeout, require vb. hepsi kullanılamaz. Byte’ları metin olarak okumak için ascii(buf) kullanın.
  • Her decode çağrısının bir çalışma süresi sınırı vardır: sonsuz bir döngü veya zaman aşımı kesintiye uğratılır ve “hiçbir şey tüketilmedi” olarak ele alınır, dolayısıyla aracı askıda bırakmaz.

Proxy yakalama sayfasına dön · İlgili: Verileri inceleme ve çözümleme · Yerel NIC yakalama