Как корректно завершить HTTP GET запрос для Server-Sent Events (SSE) в Delphi, не дожидаясь данных, и почему происходит разрыв соединения через 330 секунд?
Корректное завершение HTTP GET запроса для Server-Sent Events (SSE) в Delphi
Проблема разрыва соединения через 330 секунд
При работе с Server-Sent Events (SSE) в Delphi разработчики часто сталкиваются с проблемой автоматического разрыва соединения через 330 секунд (5,5 минут). Это происходит из-за стандартных таймаутов, установленных в компонентах HTTP-клиента.
SSE - это технология, позволяющая серверу отправлять события клиенту через одно HTTP-соединение. В отличие от WebSockets, SSE использует одностороннюю коммуникацию (от сервера к клиенту).
Основные причины проблемы
Таймаут чтения (ReadTimeout): По умолчанию в Indy (TIdHTTP) установлен таймаут 30000 мс (30 секунд)
Таймаут соединения (ConnectTimeout): Также влияет на общее время работы
Прокси и промежуточные серверы: Могут принудительно закрывать "долгие" соединения
Решение с использованием Indy (TIdHTTP)
procedure TForm1.StartSSEClient;
var
HTTPClient: TIdHTTP;
Stream: TMemoryStream;
begin
HTTPClient := TIdHTTP.Create(nil);
try
// Настройка клиента
HTTPClient.ReadTimeout := 0; // Отключаем таймаут чтения
HTTPClient.ProtocolVersion := pv1_1; // Используем HTTP 1.1
HTTPClient.Request.Connection := 'keep-alive'; // Поддержка постоянного соединения
// Обработчик для получения данных
HTTPClient.OnWork := HTTPClientWork;
Stream := TMemoryStream.Create;
try
// Отправляем запрос
HTTPClient.Get('http://example.com/sse-endpoint', Stream);
finally
Stream.Free;
end;
finally
HTTPClient.Free;
end;
end;
procedure TForm1.HTTPClientWork(ASender: TObject; AWorkMode: TWorkMode;
AWorkCount: Int64);
var
Data: string;
begin
if AWorkMode = wmRead then
begin
// Обработка полученных данных
Data := TIdHTTP(ASender).Response.ContentStream.DataString;
ProcessSSEEvent(Data);
end;
end;
Альтернативное решение с использованием THTTPClient (RTL)
procedure TForm1.StartSSEClientWithRTL;
var
HTTPClient: THTTPClient;
Response: IHTTPResponse;
Reader: TStreamReader;
begin
HTTPClient := THTTPClient.Create;
try
// Настройка клиента
HTTPClient.ConnectionTimeout := 0; // Без таймаута соединения
HTTPClient.ResponseTimeout := 0; // Без таймаута ответа
// Отправка запроса
Response := HTTPClient.Get('http://example.com/sse-endpoint');
// Чтение потока событий
Reader := TStreamReader.Create(Response.ContentStream);
try
while not Reader.EndOfStream do
begin
ProcessSSEEvent(Reader.ReadLine);
end;
finally
Reader.Free;
end;
finally
HTTPClient.Free;
end;
end;
Как корректно завершить соединение
Для принудительного завершения SSE-соединения:
С клиентской стороны:
// Для TIdHTTP
HTTPClient.Disconnect;
// Для THTTPClient
HTTPClient.CancelRequest;
С серверной стороны:
Отправьте специальное событие "close"
Закройте соединение на сервере
Рекомендации по работе с SSE в Delphi
Обработка ошибок: Всегда реализуйте обработку ошибок соединения
Переподключение: Реализуйте механизм автоматического переподключения
Таймауты: Устанавливайте разумные значения таймаутов
type
TSSEClient = class
private
FHTTPClient: TIdHTTP;
FURL: string;
FActive: Boolean;
FThread: TThread;
procedure HandleEvent(const EventData: string);
public
constructor Create(const AURL: string);
destructor Destroy; override;
procedure Start;
procedure Stop;
end;
constructor TSSEClient.Create(const AURL: string);
begin
inherited Create;
FURL := AURL;
FHTTPClient := TIdHTTP.Create(nil);
FHTTPClient.ReadTimeout := 0;
FHTTPClient.ProtocolVersion := pv1_1;
FHTTPClient.Request.Connection := 'keep-alive';
end;
destructor TSSEClient.Destroy;
begin
Stop;
FHTTPClient.Free;
inherited;
end;
procedure TSSEClient.Start;
begin
if FActive then Exit;
FActive := True;
FThread := TThread.CreateAnonymousThread(
procedure
var
Stream: TMemoryStream;
begin
Stream := TMemoryStream.Create;
try
try
FHTTPClient.Get(FURL, Stream);
except
on E: Exception do
TThread.Synchronize(nil,
procedure
begin
ShowMessage('SSE Error: ' + E.Message);
end);
end;
finally
Stream.Free;
end;
end);
FThread.FreeOnTerminate := False;
FThread.Start;
end;
procedure TSSEClient.Stop;
begin
if not FActive then Exit;
FActive := False;
FHTTPClient.Disconnect;
if Assigned(FThread) then
begin
FThread.Terminate;
FThread.WaitFor;
FThread.Free;
FThread := nil;
end;
end;
procedure TSSEClient.HandleEvent(const EventData: string);
begin
// Обработка полученного события
TThread.Queue(nil,
procedure
begin
Memo1.Lines.Add(EventData);
end);
end;
Заключение
Проблема разрыва соединения через 330 секунд в SSE-клиентах на Delphi решается правильной настройкой таймаутов HTTP-компонентов. Для стабильной работы рекомендуется:
Использовать ReadTimeout := 0 для TIdHTTP
Реализовать механизм переподключения
Корректно обрабатывать закрытие соединения
Учитывать ограничения промежуточных серверов
Приведенные примеры кода демонстрируют различные подходы к реализации SSE-клиента в Delphi, которые помогут избежать проблем с разрывом соединения.
Краткое описание проблемы разрыва соединения через 330 секунд при работе с Server-Sent Events в Delphi и методы её решения через настройку таймаутов и использование компонентов TIdHTTP или THTTPClient.
Комментарии и вопросы
Получайте свежие новости и обновления по Object Pascal, Delphi и Lazarus прямо в свой смартфон. Подпишитесь на наш Telegram-канал delphi_kansoftware и будьте в курсе последних тенденций в разработке под Linux, Windows, Android и iOS
Материалы статей собраны из открытых источников, владелец сайта не претендует на авторство. Там где авторство установить не удалось, материал подаётся без имени автора. В случае если Вы считаете, что Ваши права нарушены, пожалуйста, свяжитесь с владельцем сайта.