Eigene Protokoll-Dekodierung
Ein privates oder hauseigenes Socket-Protokoll mitgeschnitten, das die eingebauten Formate nicht erkennen können, sodass Ihnen nichts als ein Haufen Bytes bleibt? Die eigene Dekodierung ermöglicht es Ihnen, dem Tool mit einem kurzen Skript beizubringen, wie es zu lesen ist: einen fortlaufenden Byte-Strom in einzelne Nachrichten zu zerlegen, den Protokoll-Header zu entfernen, bei Bedarf zu dekomprimieren und den Rest der automatischen strukturierten Erkennung des Tools zu überlassen.
Diese Seite richtet sich an alle, die selbst einen Decoder schreiben möchten. Sie erklärt, wie man die Funktion schreibt, wann sie aufgerufen wird und was der Rückgabewert bedeutet.
1. Was es ist: ein Hook zur frameweisen Dekodierung
Abschnitt betitelt „1. Was es ist: ein Hook zur frameweisen Dekodierung“Sie schreiben einen Dekodierungs-Hook, und das Tool speist ihm die Bytes einer Verbindung ein und stellt wiederholt eine einzige Frage: “Kannst du, von hier ausgehend, eine vollständige Nachricht herausschneiden?” Sie schneiden eine Nachricht heraus und melden, wie viele Bytes Sie verbraucht haben; es fragt mit dem Rest erneut, und so weiter, bis der Strom vollständig zerlegt ist.
Wenn Sie jemals einen akkumulierenden Byte-Decoder in einem Netzwerk-Framework geschrieben haben, ist das Denkmodell dasselbe: bei einem fortlaufenden Byte-Strom bestimmen Sie die Nachrichtengrenzen selbst. Zwei zentrale Konventionen:
- Sie kümmern sich nicht um die Richtung: der Sende-Strom und der Empfangs-Strom einer Verbindung laufen jeweils getrennt durch Ihren Hook, und das Tool kennzeichnet jede zerlegte Nachricht automatisch mit ihrer Richtung. Ihr Skript behandelt nur das “Wie zerlegen”.
- Einmal zerlegt, kümmern Sie sich nicht um die Darstellung: jede Nachricht, die Sie ausgeben, durchläuft erneut die automatische Erkennung. Enthält sie protobuf, JSON oder plist, wird das weiter in eine strukturierte Form geparst. Sie sind nur für das Framing, das Entfernen von Headern und das Dekomprimieren zuständig.
2. Die decode-Funktion: decode(buf, out)
Abschnitt betitelt „2. Die decode-Funktion: decode(buf, out)“Der gesamte Decoder ist eine einzige Funktion:
function decode(buf, out) { // buf: the byte stream currently pending parse; out: the output collector; return: the number of bytes consumed this time}Was Eingaben, Ausgaben und Rückgabewert sind
Abschnitt betitelt „Was Eingaben, Ausgaben und Rückgabewert sind“buf (Eingabe): der zu parsende Byte-Strom
- Typ:
ArrayBuffer. Sein Inhalt sind alle verbleibenden Bytes “von dort, wo Sie zuletzt aufgehört haben zu verbrauchen, bis zum aktuellen Ende”. - Eigenschaft
buf.byteLength: wie viele Bytes gerade noch übrig sind (kann mit jedem Aufruf wachsen; so beurteilen Sie, ob genug da ist, um eine Nachricht herauszuschneiden). - ⚠️ Es ist ein roher Byte-Puffer, kein
Uint8Array, Sie können also nicht direkt auf Bytes zugreifen (buf[0]liefertundefined). Um Bytes zu lesen, verwenden Sie die eingebauten Hilfsfunktionen aus Abschnitt 3 (u8/u16be/sub…) oder legen Sie selbst eine Sicht darüber:new Uint8Array(buf),new DataView(buf).
out (Ausgabe): der Ausgabe-Sammler
- Typ: ein einfaches Array.
- Methode
out.push(Nachrichten-Bytes): gibt eine Nachricht aus; Sie können in einem einzigen Aufruf mehrere ausgeben. - “Nachrichten-Bytes” können sein: ein von einer eingebauten Hilfsfunktion zurückgegebener
ArrayBuffer(etwasub(...),gunzip(...)) oder ein String. Andere Typen werden ignoriert.
Rückgabewert: wie viele Bytes Sie verbraucht haben
- Typ: Ganzzahl (
number), die Anzahl der Bytes, die Sie vom Anfang vonbufverwendet haben. > 0: Sie haben so viele Bytes vom Anfang weggenommen und eine Nachricht herausgeschnitten → das Tool verwirft sie und ruft Sie erneut mit dem Rest auf.0/ keinreturn/ negative Zahl: bedeutet “am Anfang liegt noch keine vollständige Nachricht” oder “es lässt sich nichts mehr zerlegen” → das Tool stoppt die Schleife und zeigt die verbleibenden Bytes als roh an.- Der Rückgabewert wird automatisch auf die Pufferlänge begrenzt (um Überläufe zu verhindern), aber geben Sie bitte die wahre Anzahl der tatsächlich verwendeten Bytes zurück.
Wann sie aufgerufen wird (Lebenszyklus)
Abschnitt betitelt „Wann sie aufgerufen wird (Lebenszyklus)“- Das Tool führt einen Durchlauf über den Sende-Strom und einen über den Empfangs-Strom einer Verbindung durch. In jedem Durchlauf wird
decodein einer Schleife aufgerufen: jedes Mal übergibt es Ihnen die verbleibenden, nicht verbrauchten Bytes, Sie schneiden eine Nachricht heraus und geben die verbrauchte Anzahl zurück, und so weiter, bis Sie 0 zurückgeben. - Eine Nachricht erscheint erst, wenn ihr Verbrauch bestätigt ist: die Nachricht, die Sie ausgeben, taucht nur auf, wenn Sie zugleich > 0 zurückgeben. Wenn Sie ausgeben, aber 0 zurückgeben, stoppt die Schleife und die Ausgabe wird verworfen, das Ausgeben einer Nachricht muss also stets mit der Rückgabe der dafür benötigten Bytezahl einhergehen.
- Nach dem Ende der Schleife gehen nicht verbrauchte End-Bytes nicht verloren; sie werden als ein einzelnes Stück roher Daten angezeigt (zum Beispiel die zweite Hälfte einer unvollständigen Nachricht).
Zustand und Idempotenz
Abschnitt betitelt „Zustand und Idempotenz“- Jeder Strom (jede Richtung) erhält eine eigene, brandneue Skript-Umgebung; Sende-Strom und Empfangs-Strom vermischen sich nie.
- Um Zustand über Aufrufe hinweg zu halten (einen Zähler, den Typ der vorherigen Nachricht und so weiter), deklarieren Sie die Variablen außerhalb von
decode. Sie bleiben über Aufrufe hinweg innerhalb desselben Stroms erhalten und werden zurückgesetzt, wenn sich die Richtung ändert oder Sie neu dekodieren. - Die Dekodierung läuft auf bereits mitgeschnittenen Daten und kann beliebig wiederholt werden: Skript bearbeiten, speichern und erneut klicken, um dieselbe Verbindung mit den neuen Regeln neu zu dekodieren.
Fehler richten keinen Schaden an
Abschnitt betitelt „Fehler richten keinen Schaden an“- Wirft das Skript eine Ausnahme oder überschreitet ein einzelner Lauf das Zeitlimit, wird das als “diesmal nichts verbraucht” behandelt, die verbleibenden Bytes werden als rohe Daten angezeigt, und der Mitschnitt läuft weiter, ohne Datenverlust.
- Mit anderen Worten: Das Schlimmste, was ein defektes Skript anrichten kann, ist eine fehlgeschlagene Dekodierung, die Ihnen rohe Bytes hinterlässt; die Verbindung stürzt nicht ab. Bearbeiten Sie unbesorgt.
- Und Fehler werden nicht verschluckt: Ausnahmen und Zeitüberschreitungen werden im Debug-Ausgabe-Panel über den Ergebnissen angezeigt (siehe Abschnitt 5), gekennzeichnet, ob sie aus dem Sende-Strom
↑oder dem Empfangs-Strom↓stammen. Folgen Sie ihnen direkt zur Lösung.
3. Eingebaute Hilfsfunktionen (direkt im Skript verfügbar)
Abschnitt betitelt „3. Eingebaute Hilfsfunktionen (direkt im Skript verfügbar)“Die üblichen Aufgaben, Bytes lesen, Ganzzahlen lesen, in Text umwandeln und dekomprimieren, sind alle eingebaut, sodass Sie sie nicht neu erfinden müssen:
| Kategorie | Signatur | Beschreibung |
|---|---|---|
| Teil-Ausschnitt nehmen | sub(buf, off[, len]) |
Ab off schneiden (len weglassen, um bis zum Ende zu gehen), liefert ArrayBuffer |
| Ganzzahl lesen | u8(buf, off) / u16be / u16le / u32be / u32le (buf, off) |
Vorzeichenlose Ganzzahlen in Big-Endian / Little-Endian lesen |
| In Text umwandeln | hex(buf) / ascii(buf) |
In einen Hex-String umwandeln / als Text lesen |
| Kompression erkennen | gzipMagic(buf) |
Ob es gzip ist (liefert einen Boolean) |
| Dekomprimieren | gunzip / inflate / unzstd / lz4dtx (buf) |
Dekomprimieren; wenn es nicht geht, wird die Eingabe unverändert zurückgegeben, ohne Fehler |
| Debug | log(...args) / console.log(...args) |
Ins Debug-Panel ausgeben (siehe Abschnitt 5) |
Die üblichen Byte-Lese-/Schreib-Fähigkeiten (DataView, Uint8Array usw.) sind ebenfalls direkt verwendbar.
4. Beispiele
Abschnitt betitelt „4. Beispiele“① Längenpräfix [4-Byte-Big-Endian-Länge][Nutzlast], die häufigste Gestalt eines privaten Protokolls:
function decode(buf, out) { if (buf.byteLength < 4) return 0 // the length header hasn't fully arrived, wait const total = 4 + u32be(buf, 0) // whole message = 4-byte header + payload if (buf.byteLength < total) return 0 // the whole message hasn't fully arrived, wait out.push(sub(buf, 4, total - 4)) // strip the header, hand the payload to auto recognition return total // consume this message, go on to slice the next}② Nach Trennzeichen / zeilenweise, Frames an einer Marke wie einem Zeilenumbruch zerlegen:
function decode(buf, out) { const i = ascii(buf).indexOf('\n') if (i < 0) return 0 // no newline yet, wait out.push(sub(buf, 0, i)) // push this line (without the newline) return i + 1 // consume the newline along with it}③ Das Ganze auf einmal verarbeiten, den ganzen Strom als eine Nachricht behandeln, zum Beispiel das Ganze dekomprimieren:
function decode(buf, out) { out.push(gzipMagic(buf) ? gunzip(buf) : buf) // if it's gzip, unpack it return buf.byteLength // consume everything, the loop ends right away}④ Nach Typ verteilen, mit einem Typ-Feld im Header und unterschiedlicher Behandlung je Typ:
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) // the first byte is the message type const body = sub(buf, 5, total - 5) out.push(type === 2 ? gunzip(body) : body) // type 2 is compressed return total}Der Editor enthält fertige Vorlagen für all diese Muster; fügen Sie eine ein, passen Sie ein paar Zahlen an, und sie läuft.

5. Wie man es verwendet, wie man es debuggt
Abschnitt betitelt „5. Wie man es verwendet, wie man es debuggt“- Schreiben und speichern: im Decoder-Editor anlegen, benennen und speichern; eine Vorlage einfügen, um loszulegen, mit den eingebauten Funktionen als Kurzreferenz griffbereit.
- Anwenden: bei einer Verbindung, aus der Sie nicht schlau werden, rechtsklicken und “Dekodieren als” → Ihren Decoder wählen, und der gesamte Sende- und Empfangsverkehr der Verbindung wird sofort nach Ihren Regeln zerlegt und angezeigt.
- Debuggen und Variablen prüfen: verwenden Sie innerhalb von
decodelog(...)oderconsole.log(...), um jeden Wert auszugeben: Bytezahlen, Typ-Felder,hex(sub(buf, 0, 8))und so weiter. Die Ausgabe erscheint im Panel “Debug-Ausgabe” über den Ergebnissen von “Dekodieren als”, jede Zeile gekennzeichnet, ob sie aus dem Sende-Strom↑oder dem Empfangs-Strom↓stammt. Skriptfehler und Zeitüberschreitungen erscheinen im selben Panel, ebenfalls mit Richtung gekennzeichnet. Auf diese Weise ausgeben und lokalisieren; das ist weit schneller als blindes Bearbeiten.Die Sandbox hat keine vollständige Browser- / Node-
console; nurconsole.logund das gleichwertigelogsind mit diesem Panel verdrahtet.TextDecoder,fetch,setTimeoutund dergleichen sind nicht verfügbar. Um Bytes als Text zu lesen, verwenden Sieascii(buf). - Bearbeiten und live sehen: kein erneutes Mitschneiden nötig. Speichern Sie Ihr Skript, klicken Sie erneut auf “Dekodieren als”, und dieselbe Verbindung wird auf der Stelle mit den neuen Regeln neu dekodiert. Iterieren Sie, bis es so zerlegt, wie Sie es möchten.
- Weiter zur Struktur: jedes dekodierte Stück wird an die automatische Erkennung zurückgegeben, sodass protobuf, JSON und plist weiter in eine strukturierte Form geparst werden, betrachtbar über die verschiedenen Ansichten in Daten prüfen und dekodieren.
- Decoder können benannt und als Liste vorgehalten werden, um sie jederzeit hinzuzufügen, zu bearbeiten oder zu löschen.
6. Wann verwenden
Abschnitt betitelt „6. Wann verwenden“- Wenn Sie ein hauseigenes oder privates Socket-Protokoll mitschneiden (eine häufige Gestalt ist Längenpräfix + protobuf / JSON / Binärdaten), das die eingebauten Formate nicht erkennen können, schreiben Sie einen Decoder, um es zu zerlegen und in eine lesbare Struktur zurückzuführen.
- Wenn Sie einen Batzen Bytes, die in einen eigenen Header eingewickelt sind oder eine Runde Kompression durchlaufen haben, wieder in lesbaren Inhalt verwandeln möchten.
7. API-Referenz
Abschnitt betitelt „7. API-Referenz“Eine gebündelte Kurzreferenz: der Einstiegspunkt decode, die eingebauten Hilfsfunktionen und die Laufzeitumgebung. Jede Funktion, die buf entgegennimmt, akzeptiert entweder einen ArrayBuffer oder einen String.
Einstiegspunkt der Dekodierung
Abschnitt betitelt „Einstiegspunkt der Dekodierung“decode(buf, out) → number| Zweck | Der einzige Einstiegspunkt des Decoders, muss implementiert werden. Nachrichten vom Anfang von buf herausschneiden, sie per push an out geben und die diesmal verbrauchte Bytezahl zurückgeben. |
buf |
ArrayBuffer, der verbleibende, zu parsende Byte-Strom. Verwenden Sie buf.byteLength für seine Länge; Sie können nicht direkt auf Bytes zugreifen, nutzen Sie die Hilfsfunktionen unten oder new DataView(buf) / new Uint8Array(buf). |
out |
Array, der Ausgabe-Sammler. out.push(bytes) gibt eine Nachricht aus (bytes ist ein ArrayBuffer oder ein String; andere Typen werden ignoriert); Sie können mehrere auf einmal ausgeben. |
| Rückgabe | number, die vom Anfang von buf verbrauchten Bytes. > 0 fährt fort; 0 / negativ / keine Rückgabe → stoppen, und verbleibende Bytes werden als roh angezeigt. |
| Aufruf-Konvention | Je einen Durchlauf über Sende-Strom und Empfangs-Strom der Verbindung; jeder Durchlauf ruft wiederholt auf und übergibt jedes Mal die verbleibenden, nicht verbrauchten Bytes, bis Sie 0 zurückgeben. |
Eingebaute Hilfsfunktionen
Abschnitt betitelt „Eingebaute Hilfsfunktionen“Teil-Ausschnitt nehmen
| Signatur | Rückgabe | Beschreibung |
|---|---|---|
sub(buf, off) |
ArrayBuffer |
Von off bis zum Ende schneiden |
sub(buf, off, len) |
ArrayBuffer |
len Bytes ab off schneiden; ein außerhalb des Bereichs liegendes off / len wird automatisch begrenzt, kein Fehler |
Ganzzahl lesen (vorzeichenlos; liefert 0, wenn off außerhalb des Bereichs liegt)
| Signatur | Rückgabe | Beschreibung |
|---|---|---|
u8(buf, off) |
number |
1 Byte lesen |
u16be(buf, off) / u16le(buf, off) |
number |
2 Bytes lesen, Big-Endian / Little-Endian |
u32be(buf, off) / u32le(buf, off) |
number |
4 Bytes lesen, Big-Endian / Little-Endian |
In Text umwandeln
| Signatur | Rückgabe | Beschreibung |
|---|---|---|
hex(buf) |
string |
In kleingeschriebenes Hexadezimal umwandeln |
ascii(buf) |
string |
Als Text auslesen (geeignet für ASCII- / Text-Inhalte) |
Kompression / Dekompression (liefert die Eingabe unverändert zurück, wenn nicht dekomprimiert werden kann; Obergrenze pro Dekompression rund 16 MB)
| Signatur | Rückgabe | Beschreibung |
|---|---|---|
gzipMagic(buf) |
boolean |
Ob es mit der gzip-Magic-Zahl beginnt |
gunzip(buf) |
ArrayBuffer |
gzip-Dekompression |
inflate(buf) |
ArrayBuffer |
zlib- / deflate-Dekompression |
unzstd(buf) |
ArrayBuffer |
zstd-Dekompression |
lz4dtx(buf) |
ArrayBuffer |
LZ4-Block-Strom-Dekompression |
Debug
| Signatur | Rückgabe | Beschreibung |
|---|---|---|
log(...args) |
(keine) | Fügt die Argumente zu einer Zeile zusammen und gibt sie ins Panel “Debug-Ausgabe” aus; ein ArrayBuffer wird hexadezimal angezeigt |
console.log(...args) |
(keine) | Wie log (praktisch für alle, die console.log gewohnt sind) |
Laufzeitumgebung
Abschnitt betitelt „Laufzeitumgebung“- Standard-JavaScript: gängige eingebaute Objekte wie
ArrayBuffer, typisierte Arrays (Uint8Arrayusw.),DataView,JSON,Math,RegExp,Datesind alle verfügbar; wenn Sie byteweises Lesen/Schreiben auf niedrigerer Ebene brauchen, stehennew DataView(buf)/new Uint8Array(buf)bereit. - Debug:
log(...)/console.log(...)geben ins Panel “Debug-Ausgabe” aus; Skriptfehler und Zeitüberschreitungen werden ebenfalls dort ausgegeben, gekennzeichnet, ob sie aus dem Sende-Strom↑oder dem Empfangs-Strom↓stammen. - Nicht bereitgestellt sind die übrigen browser- / Node-spezifischen Fähigkeiten:
console-Methoden außerlog,TextDecoder/TextEncoder,fetch,setTimeout,requireusw. sind allesamt nicht verfügbar. Um Bytes als Text zu lesen, verwenden Sieascii(buf). - Jeder
decode-Aufruf hat eine Obergrenze für die Ausführungszeit: eine Endlosschleife oder eine Zeitüberschreitung wird unterbrochen und als “nichts verbraucht” behandelt, sodass sie das Tool nicht hängen lässt.
Zurück zu Proxy-Capture · Verwandt: Daten prüfen und dekodieren · Lokales NIC-Capture