TsgcQUICServer ist ein Low-Level-QUIC-Server: Er lauscht auf QUIC-Verbindungen über UDP und tauscht rohe Anwendungsbytes über QUIC-Streams aus.
TsgcQUICServer implementiert die Serverseite von QUIC (RFC 9000, mit TLS 1.3 gemäß RFC 9001 und Verlustbehebung gemäß RFC 9002). QUIC ist ein verbindungsorientierter, multiplexter, stets verschlüsselter Transport, der über UDP läuft. Diese Komponente stellt ihn als Byte-Transport bereit: Sie übergibt Ihnen die Bytes, die ein Peer sendet, und lässt Sie Bytes zurückschreiben. Sie implementiert kein darauf aufsetzendes Anwendungsprotokoll. Für HTTP/3 verwenden Sie TsgcHTTP3Server, das von dieser Komponente abgeleitet ist.
Was Ihnen die Komponente bietet:
Start eine Ausnahme aus.h3 und akzeptiert andernfalls das erste Angebot des Peers, er ist nicht konfigurierbar. Ein Peer, der nichts anbietet, wird abgelehnt.
Der QUIC-Server läuft standardmäßig auf der integrierten QUIC-Engine von OpenSSL 3.5+. Er ruft OSSL_QUIC_server_method und SSL_new_listener auf, wenn die geladene OpenSSL-Bibliothek diese exportiert. Andernfalls fällt er auf die sgc-QUIC-Engine zurück, und mit QUICOptions.SSLOptions.IOHandler = iohNativeTLS verwendet er immer die sgc-Engine mit dem TLS-1.3-Handshake auf der nativen TLS-Engine. Die schreibgeschützte Eigenschaft QUICMode meldet die beim Start gewählte Engine. Siehe QUIC für den Engine-Vergleich.
Befolgen Sie diese Schritte, um die Komponente zu konfigurieren:
1. Erstellen Sie eine neue Instanz von TsgcQUICServer.
2. Legen Sie den Port fest, auf dem gelauscht wird. Standardmäßig ist er 80, geerbt von der UDP-Basisklasse, setzen Sie ihn daher immer (4433 und 443 sind die üblichen QUIC-Werte).
3. Legen Sie das Zertifikat und den privaten Schlüssel in QUICOptions.SSLOptions fest. Beide sind PEM-Dateien und beide sind zwingend erforderlich. Für ein Testzertifikat:
openssl req -x509 -newkey rsa:2048 -keyout server.key -out server.crt -days 365 -nodes -subj "/CN=localhost"
4. Behandeln Sie OnQUICConnect und OnQUICData und rufen Sie Start innerhalb eines try..except auf, weil Konfigurationsfehler dort auftreten.
oServer := TsgcQUICServer.Create(nil);
oServer.Port := 4433;
oServer.QUICOptions.SSLOptions.CertFile := 'server.crt';
oServer.QUICOptions.SSLOptions.KeyFile := 'server.key';
oServer.OnQUICConnect := OnQUICConnectEvent;
oServer.OnQUICData := OnQUICDataEvent;
oServer.Start;
procedure TfrmServer.OnQUICDataEvent(Sender: TObject;
const aConnection: TsgcQUICServerConnection; const aData: TBytes);
begin
// echo the received bytes back to the peer
oServer.WriteData(aConnection, aData);
end;
Threading. Jedes Ereignis wird im Hintergrund-Listener-Thread des Servers ausgelöst, und dieser eine Thread treibt die Ereignisschleife für alle Verbindungen an. Halten Sie Handler daher kurz und marshallen Sie zum Haupt-Thread, bevor Sie die UI ansprechen.
Lebenszyklus. Verwenden Sie Start und Stop oder die Eigenschaft Active, auf der sie aufbauen und die den tatsächlichen Listener-Zustand meldet. OnStartup und OnShutdown werden um diese Übergänge herum ausgelöst, sodass Sie aus einem Handler heraus oder nach der Rückkehr des Aufrufs reagieren können, je nachdem, was Ihnen besser passt.
TsgcHTTP3Server_Base ist von TsgcQUICServer_Base abgeleitet, sodass TsgcHTTP3Server ein QUIC-Server mit darauf aufgesetztem HTTP/3-Protokoll ist. Verwenden Sie TsgcQUICServer, wenn Sie einen rohen Byte-Transport mit Ihrem eigenen Protokoll darauf möchten, und TsgcHTTP3Server, wenn Sie HTTP/3 ausliefern möchten.
Wenn Sie den HTTP/3-Server verwenden, beachten Sie, dass er einen Teil der Oberfläche dieser Komponente beansprucht: Sein Konstruktor setzt AlwaysMultiStream auf True (HTTP/3 benötigt die unidirektionalen Control- und QPACK-Streams des Clients, RFC 9114) und verdrahtet OnQUICConnect, OnQUICData, OnQUICStreamData und OnQUICDisconnect mit seinen eigenen Handlern. Das Zuweisen dieser Ereignisse oder das Zurücksetzen von AlwaysMultiStream beschädigt die HTTP/3-Schicht. OnQUICException ist die eine Ausnahme, der HTTP/3-Server beansprucht es nur, wenn Sie es nicht selbst zugewiesen haben.
Zwei Klassen vervollständigen die API:
| Name | Beschreibung |
|---|---|
| QUICOptions | Die gesamte Serverkonfiguration: das obligatorische TLS-Zertifikat und der private Schlüssel sowie das Verbindungslimit. |
| Port | UDP-Port, an den sich der Server bindet. Standardmäßig 80, setzen Sie ihn daher immer. |
| AlwaysMultiStream | Jeden Stream annehmen und lesen, den der Peer öffnet, nicht nur den ersten. |
| Connections | Die Liste der QUIC-Verbindungen, die derzeit vom Server akzeptiert sind. |
| Name | Beschreibung |
|---|---|
| Start | Lädt OpenSSL, erstellt den QUIC-Listener und beginnt, Verbindungen anzunehmen. |
| Stop | Beendet das Lauschen, schließt jede aktive Verbindung und gibt den QUIC-Listener frei. |
| WriteData | Sendet Anwendungs-Bytes an einen verbundenen Peer über dessen Standard-QUIC-Stream. |
| OpenStream | Öffnet einen neuen, vom Server initiierten QUIC-Stream auf einer Verbindung. |
| Name | Beschreibung |
|---|---|
| OnQUICConnect | Wird ausgelöst, wenn eine neue QUIC-Verbindung angenommen wurde und ihr erster Stream bereit ist. |
| OnQUICData | Wird ausgelöst, wenn Anwendungsdaten von einem Peer über dessen Standard-QUIC-Stream empfangen werden. |
| OnQUICStreamData | Wird mit Bytes ausgelöst, die von einem zusätzlichen QUIC-Stream gelesen wurden, versehen mit dessen Stream-ID. |
| OnQUICDisconnect | Wird ausgelöst, wenn eine QUIC-Verbindung geschlossen wurde. |
Ein Echo-Server, der Verbindungen und Fehler meldet. Beachten Sie, dass PeerIP vom Delphi-Server nicht gefüllt wird, da OpenSSL keinen Getter dafür bereitstellt, die Sitzung wird daher über die Verbindungsinstanz identifiziert.
uses
sgcQUIC_Server, sgcHTTP;
procedure TfrmServer.StartServer;
begin
oServer := TsgcQUICServer.Create(nil);
oServer.Port := 4433;
// mandatory: QUIC always runs over TLS 1.3
oServer.QUICOptions.SSLOptions.CertFile := 'server.crt';
oServer.QUICOptions.SSLOptions.KeyFile := 'server.key';
oServer.QUICOptions.MaxConnections := 1000;
oServer.OnQUICConnect := OnQUICConnectEvent;
oServer.OnQUICData := OnQUICDataEvent;
oServer.OnQUICDisconnect := OnQUICDisconnectEvent;
oServer.OnQUICException := OnQUICExceptionEvent;
oServer.OnStartup := OnStartupEvent;
// Start raises on a bad configuration, OnStartup fires once it is listening
try
oServer.Start;
except
on E: Exception do
Log('start failed: ' + E.Message);
end;
end;
procedure TfrmServer.StopServer;
begin
oServer.Stop; // OnQUICDisconnect is NOT raised for these connections
Log('QUIC server stopped');
oServer.Free;
end;
procedure TfrmServer.OnStartupEvent(Sender: TObject);
begin
// raised once the listener is bound and accepting
Log('QUIC server listening on UDP port ' + IntToStr(oServer.Port));
end;
procedure TfrmServer.OnQUICConnectEvent(Sender: TObject;
const aConnection: TsgcQUICServerConnection);
begin
Log(Format('connected at %s (%d live)',
[DateTimeToStr(aConnection.CreatedAt), oServer.ConnectionCount]));
end;
procedure TfrmServer.OnQUICDataEvent(Sender: TObject;
const aConnection: TsgcQUICServerConnection; const aData: TBytes);
begin
// aData is a chunk, not a message: frame at the application level
oServer.WriteData(aConnection, aData);
end;
procedure TfrmServer.OnQUICDisconnectEvent(Sender: TObject;
const aConnection: TsgcQUICServerConnection; aCode: Int64; const aReason: string);
begin
// aCode 0 / empty reason on a normal close, otherwise an OpenSSL error code
Log(Format('disconnected (code=%d %s)', [aCode, aReason]));
end;
procedure TfrmServer.OnQUICExceptionEvent(Sender: TObject;
const aConnection: TsgcQUICServerConnection; const aException: Exception);
begin
// aConnection is nil for a listener error, otherwise it is the failing session
if Assigned(aConnection) then
Log('connection error: ' + aException.Message)
else
Log('listener error: ' + aException.Message);
end;
| Voraussetzung | Detail |
|---|---|
| Edition | All-Access-Edition. |
| Compiler-Define | QUIC wird deaktiviert ausgeliefert. Entfernen Sie im All-Access-Block von sgcVer.inc den Kommentar vor {$DEFINE SGC_PACK_HTTP3}, ausgeliefert wird es als {.$DEFINE SGC_PACK_HTTP3}. Dieser Block definiert SGC_QUIC, SGC_HTTP3 und SGC_WEBTRANSPORT, aber nur, wenn auch SGC_INDY_LIB definiert ist. |
| Unit | sgcQUIC_Server für die Klassen, sgcHTTP für die registrierte Komponente TsgcQUICServer. |
| OpenSSL | OpenSSL 3.5 oder neuer mit IOHandler = iohOpenSSL. Die integrierte Engine verwendet die native QUIC-Listener-API (OSSL_QUIC_server_method, SSL_new_listener) und die sgc-Engine verwendet die OpenSSL-3.5-QUIC-TLS-API, keine von beiden gibt es in früheren Versionen. Mit iohNativeTLS wird kein OpenSSL benötigt. QUICOptions.SSLOptions.OpenSSL_Options.APIVersion muss oslAPI_3_0 bleiben. |
| TLS | Ein Zertifikat und ein passender privater Schlüssel, beide im PEM-Format, sind zwingend erforderlich. Ein verschlüsselter privater Schlüssel wird unterstützt, setzen Sie dessen Passphrase in QUICOptions.SSLOptions.Password. |
| .NET | net8.0 oder höher und MsQuic zur Laufzeit. Der QUIC-Server existiert aus Gründen der Parität in jedem Ziel-Framework, aber unter netstandard2.0 löst Start() eine PlatformNotSupportedException aus. |