TsgcWebTransportStream › Ereignisse › OnData
Wird ausgelöst, wenn Anwendungsbytes auf diesem Stream eintreffen. So empfangen Sie in WebTransport alles.
property OnData: TsgcWebTransportStreamDataEvent read FOnData write FOnData;
// event type
TsgcWebTransportStreamDataEvent = procedure(Sender: TObject;
const aData: TBytes) of object;
// handler signature
procedure MyStreamData(Sender: TObject; const aData: TBytes);
| Name | Typ | Beschreibung |
|---|---|---|
Sender | TObject | Der TsgcWebTransportStream, auf dem die Bytes eingetroffen sind. Casten Sie ihn, um auf StreamId, Unidirectional oder Session zuzugreifen, auf diese Weise kann ein Handler viele Streams bedienen. |
aData | const TBytes | Ausschließlich Anwendungsbytes. Der WebTransport-Stream-Header wurde bereits abgetrennt, dies ist also genau das, was der Peer an sein eigenes WriteData übergeben hat, vorbehaltlich des unten beschriebenen Chunking-Vorbehalts. |
Nicht zugewiesen. Wenn kein Handler zugewiesen ist, werden eingehende Bytes auf diesem Stream stillschweigend verworfen.
Weisen Sie es zu, bevor etwas eintreffen kann. Bei einem vom Peer geöffneten Stream bedeutet das innerhalb des OnStream-Handlers der Sitzung, bevor dieser zurückkehrt: Bytes, die im selben Lesevorgang wie der Stream-Header eingetroffen sind, werden in dem Moment zugestellt, in dem der Handler zurückkehrt, und verworfen, wenn kein Handler gesetzt ist. Bei einem Stream, den Sie selbst geöffnet haben, weisen Sie es vor dem ersten WriteData zu, da ein bidirektionaler Peer sofort antworten kann.
Auf dem Client gilt: pumpen, sonst bekommen Sie nichts. Die QUIC-Verbindung ist nicht-blockierend, daher wird OnData nur ausgelöst, während TsgcHTTP3Client.WebTransportProcessEvents von einem Timer oder Thread aus aufgerufen wird. Ein Client, der OnData zuweist und nie pumpt, sieht überhaupt keine Daten, ohne Fehler. Dies gilt nicht für den Server und nicht für .NET.
Nachrichtengrenzen bleiben nicht erhalten. Der wichtigste Vorbehalt an dieser Stelle. Jeder Aufruf liefert Ihnen die Bytes, die der Transport gerade zur Verfügung hatte: Ein WriteData des Peers kann als mehrere OnData-Aufrufe erscheinen, und mehrere können zu einem zusammengefasst eintreffen. Behandeln Sie ein OnData nicht als eine Nachricht. Wenn Ihr Protokoll Nachrichten kennt, sammeln Sie die Bytes und rahmen Sie sie selbst. Kurze Test-Payloads treffen in der Regel vollständig ein, sodass dies selten auffällt, bis die Nachrichten größer werden.
Antworten. Auf einem bidirektionalen Stream antwortet der Aufruf von WriteData des Sender innerhalb des Handlers auf demselben Stream, und genau das ist das Echo-Muster, das die Demos verwenden. Auf einem vom Peer geöffneten unidirektionalen Stream können Sie nicht antworten: Prüfen Sie zuerst Unidirectional.
Threading. In Delphi und C++Builder läuft der Handler auf dem Thread, der die Daten hereingepumpt hat: Ihr Timer- oder Worker-Thread auf dem Client, ein Verbindungs-Thread auf dem Server. Nichts wird mit dem Haupt-Thread synchronisiert, marshallen Sie UI-Aktualisierungen daher mit TThread.Synchronize oder TThread.Queue. Unter .NET läuft er auf einem Thread-Pool-Thread aus der Leseschleife des Streams, verwenden Sie daher Invoke für WinForms oder WPF. Halten Sie den Handler in jedem Fall kurz: Er läuft auf dem Lesepfad, und ihn zu blockieren bringt die Verbindung zum Stocken.
aData ist für die Dauer des Aufrufs gültig. Kopieren Sie es, wenn Sie es behalten müssen. Ein leeres Array ist möglich, wenn ein Transport eines liefert, obwohl die bibliothekseigenen Aufrufer zuerst die Länge prüfen.
// Assign as soon as you have the stream, and before you write
FStream := FSession.OpenStream(False);
FStream.OnData := OnStreamData;
FStream.WriteData('Hello WebTransport');
tmrPump.Enabled := True; // without this, OnData never fires
procedure TfrmClient.tmrPumpTimer(Sender: TObject);
begin
FClient.WebTransportProcessEvents;
end;
procedure TfrmClient.OnStreamData(Sender: TObject; const aData: TBytes);
var
oStream: TsgcWebTransportStream;
begin
if not (Sender is TsgcWebTransportStream) then
Exit;
oStream := TsgcWebTransportStream(Sender);
// Sender identifies the stream, so one handler serves them all
memoLog.Lines.Add(Format('stream %d: %d bytes: %s',
[oStream.StreamId, Length(aData), TEncoding.UTF8.GetString(aData)]));
// Echo, but only where a reply is possible
if not oStream.Unidirectional then
oStream.WriteData(aData);
end;
// Framing: OnData does NOT preserve message boundaries, so a real protocol
// accumulates bytes and cuts messages out of the buffer itself.
procedure TfrmClient.OnFramedData(Sender: TObject; const aData: TBytes);
var
vLen: Integer;
vMsg: TBytes;
begin
FBuffer := FBuffer + aData; // TBytes concatenation, D12+
while Length(FBuffer) >= 4 do
begin
vLen := (FBuffer[0] shl 24) or (FBuffer[1] shl 16) or
(FBuffer[2] shl 8) or FBuffer[3];
if Length(FBuffer) < 4 + vLen then
Break; // message not complete yet, wait for more
vMsg := Copy(FBuffer, 4, vLen);
FBuffer := Copy(FBuffer, 4 + vLen, Length(FBuffer) - 4 - vLen);
memoLog.Lines.Add('message: ' + TEncoding.UTF8.GetString(vMsg));
end;
end;