Les Server-Sent Events étaient autrefois le cousin discret des WebSockets : une simple réponse HTTP qui ne se termine jamais, le serveur écrivant un petit événement texte après l'autre. Ils sont soudain partout. Les réponses en streaming des API de LLM arrivent en SSE, les serveurs MCP le parlent via Streamable HTTP, et les tableaux de bord, les notifications et les flux de progression de tâches l'utilisent parce qu'il traverse tous les proxys qui laissent passer le HTTP.
sgcWebSockets 2026.10 ajoute TsgcSSEClient, un client EventSource pour Delphi, C++Builder et .NET. Il lit le flux dans un thread en arrière-plan, vous livre chaque événement avec son type, ses données et son id, se reconnecte de lui-même quand la connexion tombe, et reprend avec Last-Event-ID pour que rien ne soit perdu entre-temps. Le composant ne fait pas partie du téléchargement actuel. Il arrive avec la version 2026.10, dans toutes les éditions.
TsgcSSEClient recevant des événements typés, perdant la connexion et reprenant avec Last-Event-ID. Également sur YouTube.
Pourquoi SSE compte maintenant
Un flux SSE est une réponse text/event-stream composée de blocs courts : une ligne event: facultative avec le type, une ou plusieurs lignes data:, un id: facultatif, et une ligne vide qui termine l'événement. Un serveur peut aussi envoyer retry: pour indiquer au client combien de temps attendre avant de se reconnecter. Le format est simple, mais un client correct doit gérer des fragments qui coupent une ligne n'importe où, des données multilignes, des fins de ligne CR, LF et CRLF, et une reconnexion qui se souvient d'où elle s'est arrêtée. C'est la partie dont TsgcSSEClient s'occupe pour vous.
Démarrage rapide
Ajoutez l'unit sgcHTTP_SSE_Client, définissez l'URL, gérez OnEvent et appelez Open. Les en-têtes de requête, comme un jeton bearer, vont dans Headers, une ligne Name: Value chacune, et sont envoyés à chaque connexion, reconnexions comprises.
procedure TForm1.FormCreate(Sender: TObject);
begin
oSSE := TsgcSSEClient.Create(nil);
oSSE.URL := 'https://www.example.com/events';
oSSE.Headers.Add('Authorization: Bearer ' + FToken);
oSSE.OnEvent := OnSSEEvent;
oSSE.OnError := OnSSEError;
oSSE.Open;
end;
procedure TForm1.OnSSEEvent(Sender: TObject; const aEvent: TsgcSSEEvent);
begin
if aEvent.EventType = 'alert' then
ShowMessage(aEvent.Data)
else
Memo1.Lines.Add(aEvent.EventType + ' #' + aEvent.Id + ': ' + aEvent.Data);
end;
procedure TForm1.OnSSEError(Sender: TObject; const aError: string);
begin
Memo1.Lines.Add('error: ' + aError);
end;
Chaque événement est typé. EventType porte le nom event:, si bien qu'un seul gestionnaire peut router alert, tick ou tout type inventé par votre serveur. Dans une application VCL ou FireMonkey, les événements sont livrés au thread principal par défaut, si bien que les gestionnaires peuvent toucher l'interface directement. NotifyEvents change cela. OnOpen et OnClose vous indiquent quand le flux commence et se termine, et ReadyState rapporte sseConnecting, sseOpen ou sseClosed.
Le client réagit au serveur de la même façon que l'EventSource d'un navigateur :
- Un 200 avec
text/event-streamouvre le flux. Les redirections sont suivies. - Un 204 No Content signifie que le serveur veut que le client s'arrête. Il se ferme sans erreur et ne se reconnecte pas.
- Tout autre statut, ou un mauvais type de contenu, déclenche
OnErrorpuisOnClose, et le client ne se reconnecte pas. Réessayer un 401 indéfiniment n'aide personne. - Une erreur réseau ou un délai de lecture dépassé déclenche
OnError, puis le client se reconnecte. Quand le serveur termine simplement le flux, le client se reconnecte aussi.
Reprendre avec Last-Event-ID
Quand le serveur donne à ses événements un champ id:, le client garde le dernier dans LastEventId et l'envoie dans l'en-tête Last-Event-ID à chaque reconnexion. Le serveur lit l'en-tête et continue après cet événement, si bien qu'une connexion perdue ne coûte aucun message et n'en répète aucun. À l'intérieur d'une même session, cela ne nécessite aucun code.
Pour reprendre après le redémarrage de l'application, enregistrez LastEventId quand vous arrêtez et restaurez-le avant Open. La propriété conserve sa valeur après Close, et une valeur définie avant Open est envoyée avec la toute première requête.
procedure TForm1.StartStream;
begin
if FileExists('sse_last_id.txt') then
oSSE.LastEventId := Trim(TFile.ReadAllText('sse_last_id.txt'));
oSSE.Open;
end;
procedure TForm1.StopStream;
begin
oSSE.Close;
TFile.WriteAllText('sse_last_id.txt', oSSE.LastEventId);
end;
Reprendre nécessite la coopération du serveur : il doit envoyer id: avec ses événements et respecter l'en-tête Last-Event-ID.
Une politique de reconnexion que vous contrôlez
La reconnexion est activée par défaut, avec un délai de 3 secondes. ReconnectOptions en fait la politique que votre serveur mérite : backoff exponentiel avec un multiplicateur, un plafond pour le délai, un jitter aléatoire pour que mille clients ne reviennent pas à la même milliseconde, et une limite de tentatives.
procedure TForm1.SetupReconnect;
begin
oSSE.ReconnectOptions.Interval := 1000;
oSSE.ReconnectOptions.Backoff := True;
oSSE.ReconnectOptions.BackoffMultiplier := 2.0;
oSSE.ReconnectOptions.MaxInterval := 30000;
oSSE.ReconnectOptions.Jitter := 0.2;
oSSE.ReconnectOptions.MaxAttempts := 10;
oSSE.OnReconnect := OnSSEReconnect;
end;
MaxAttempts compte les échecs consécutifs, et le compteur repart à zéro à chaque ouverture de connexion, si bien qu'un client qui tourne pendant des semaines avec quelques incidents réseau ne manque jamais de tentatives. Le serveur a aussi son mot à dire : un champ retry: remplace Interval pour le reste de la session. Le dernier mot vous revient, dans OnReconnect, qui se déclenche avant chaque tentative avec le délai calculé :
procedure TForm1.OnSSEReconnect(Sender: TObject; aAttempt: Integer;
var aDelay: Integer; var aCancel: Boolean);
begin
// stop after the fifth failed attempt, otherwise wait at least 2 seconds
if aAttempt > 5 then
aCancel := True
else if aDelay < 2000 then
aDelay := 2000;
end;
TLS, proxy et authentification
Le flux est lu avec un TsgcHTTP1Client, et OnBeforeConnect vous le remet avant chaque tentative de connexion. Tout ce que le client HTTP peut faire, le client SSE peut le faire : options TLS, un proxy, une authentification de base ou un délai de lecture plus long.
procedure TForm1.OnSSEBeforeConnect(Sender: TObject;
const aHTTP: TsgcHTTP1Client);
begin
aHTTP.TLSOptions.Version := tls1_2;
aHTTP.Proxy.Enabled := True;
aHTTP.Proxy.Host := '192.168.1.10';
aHTTP.Proxy.Port := 8080;
aHTTP.ReadTimeout := 60000;
end;
OnBeforeConnect et OnReconnect s'exécutent toujours dans le thread en arrière-plan, donc gardez l'interface en dehors d'eux.
Le parseur pour les flux LLM et MCP
Souvent, vous n'avez pas du tout besoin d'un EventSource de longue durée. Une complétion de chat LLM avec stream: true est un POST dont la réponse est en SSE, et une réponse Streamable HTTP de MCP peut aussi être en SSE. Pour ces cas, le parseur à l'intérieur du client est public : TsgcSSEParser. Alimentez-le avec des octets bruts ou du texte en fragments de n'importe quelle taille, exactement comme le réseau les livre, et il déclenche OnEvent pour chaque événement complet et OnRetry pour chaque champ retry: valide. Un fragment peut se terminer au milieu d'une ligne ou d'un caractère UTF-8, et le parseur conserve le reste jusqu'au prochain appel.
procedure TForm1.ParseStream;
begin
FParser := TsgcSSEParser.Create;
FParser.OnEvent := OnParserEvent;
// chunks arrive as the network delivers them, split anywhere
FParser.Feed('event: content_block_delta'#10'data: {"delta":{"text":"Hel');
FParser.Feed('lo"}}'#10#10'event: message_stop'#10);
FParser.Feed(TEncoding.UTF8.GetBytes('data: {}'#10#10));
// start again for the next response
FParser.Reset;
end;
procedure TForm1.OnParserEvent(Sender: TObject; const aEvent: TsgcSSEEvent);
begin
Memo1.Lines.Add(aEvent.EventType + ' ' + aEvent.Data);
end;
Ces trois fragments produisent deux événements : content_block_delta avec le JSON complet {"delta":{"text":"Hello"}}, et message_stop. Reset élimine tout événement lu à moitié avant la réponse suivante, et LastEventId vous indique le dernier id vu par le parseur.
C# pour .NET
L'édition .NET de sgcWebSockets a le même composant avec les mêmes noms, dans l'espace de noms esegece.sgcWebSockets. Headers est une liste de chaînes Name: Value et les événements sont des événements .NET classiques.
using esegece.sgcWebSockets;
string token = args.Length > 0 ? args[0] : "";
var sse = new TsgcSSEClient();
sse.URL = "https://www.example.com/events";
sse.Headers.Add("Authorization: Bearer " + token);
if (File.Exists("sse_last_id.txt"))
sse.LastEventId = File.ReadAllText("sse_last_id.txt").Trim();
sse.ReconnectOptions.Backoff = true;
sse.ReconnectOptions.MaxInterval = 30000;
sse.OnEvent += (sender, e) => Console.WriteLine($"{e.EventType} #{e.Id}: {e.Data}");
sse.OnError += (sender, error) => Console.WriteLine("error: " + error);
sse.Open();
Console.ReadLine();
sse.Close();
File.WriteAllText("sse_last_id.txt", sse.LastEventId);
TsgcSSEParser est là aussi, avec des surcharges de Feed pour un byte[], une tranche de celui-ci, ou une chaîne.
Essayez les démos
Les deux éditions incluent une démo qui fonctionne entièrement hors ligne. Elle héberge un petit serveur SSE au sein de la même application sur 127.0.0.1, port 5580, qui diffuse des événements numérotés avec un id:, fait tourner les types message, tick et alert, envoie un champ retry: et reprend après le Last-Event-ID qu'il reçoit. Coupez la connexion et observez le client se reconnecter et continuer à partir de l'id suivant, sans trou ni doublon.
- Delphi :
Demos\20.HTTP_Protocol\17.SSE_Client_Component - .NET :
demos\20.HTTP_Protocol\17.SSE_Client_Component, qui dispose aussi d'un commutateur/selftestqui vérifie de lui-même l'ordre, les doublons et l'en-tête de reprise
Disponibilité
TsgcSSEClient et TsgcSSEParser arrivent dans sgcWebSockets 2026.10, pour Delphi, C++Builder et .NET, dans toutes les éditions. Ils ne sont pas dans la version que vous pouvez télécharger aujourd'hui. Quand la 2026.10 sortira, elle sera sur la page de téléchargement, et le composant sera enregistré dans l'onglet de palette SGC HTTP.
La référence complète, avec chaque propriété, événement et le guide de reprise, se trouve dans l'aide de TsgcSSEClient. Pour tout le reste de ce que sgcWebSockets fait avec les Server-Sent Events, consultez la page produit SSE.
À lire aussi
Des questions, des retours ou besoin d'aide pour la migration ? Contactez-nous. Vous recevrez une réponse des personnes qui ont écrit le code.
