В данной статье мы рассмотрим методы организации одноканального ввода потока данных от одного клиента, который будет распространяться между несколькими другими подключенными клиентами. Это типичная задача для многосервисных веб-приложений и игр, где сервер действует как посредник, перенаправляя потоковые данные на просмотрщиков или пользователей в реальном времени.
Основная проблема
Проблема заключается в том, чтобы организовать передачу данных от одного клиента к нескольким через интернет. Технология, которую вы используете для этого — это Indy 10 с компонентом TIdTCPServer. Ваш TCP-сервер должен получать поток данных от одного клиента и быстро распространять его среди уже подключенных клиентов, действующих как "просмотрщики" или "слушатели".
Решение на основе Indy
Важное замечание: протоколы TCP/IP не поддерживают вещание в прямом смысле слова. Вместо этого вам нужно будет обрабатывать каждого получателя по отдельности, что может быть выполнено непосредственно с помощью события OnExecute компонента сервера.
Однако, если вы хотите избежать задержек для отправляющего клиента, необходимо использовать параллельные потоки. В таком случае, вам следует поместить полученные блоки данных в безопасный для многопоточности очередь и организовать отдельный поток для рассылки этих блоков каждому подключённому клиенту.
Пример кода на Object Pascal (Delphi)
type
TDataQueue = class(TInterfacedObject, IInterface)
private
FQueue: TThreadSafeQueue;
constructor Create; override;
destructor Destroy; override;
procedure AddToQueue(const AData: TStream); safecall;
public
function GetFromQueue(out Result: TStream): boolean; safecall;
end;
constructor TDataQueue.Create;
begin
inherited Create;
FQueue := TThreadSafeQueue.Create(TStream);
end;
destructor TDataQueue.Destroy;
begin
FQueue.Free;
inherited Destroy;
end;
procedure TDataQueue.AddToQueue(const AData: TStream);
begin
FQueue.Push(AData);
end;
function TDataQueue.GetFromQueue(out Result: TStream): boolean;
begin
Result := nil;
if not FQueue.Count = 0 then
Result := (FQueue.Pop as TStream)
Result <> nil; // Возвращаем истину, если данные были успешно извлечены
end;
procedure TServerForm.IdTCPServer1Execute(AContext: TIdContext);
var
DataBuffer: TMemoryStream;
begin
inherited;
AContext.Connection.IOHandler.ReadDirect(DataBuffer, AContext.Connection.IOHandler.InputBufferSize);
with TDataQueue.Create(nil) do try
AddToQueue(DataBuffer);
for var Client in IdTCPServer1.Contexts do
if Assigned(Client.Connection) then begin
with TDataQueue.Create(nil) do try
Client.DataQueue := self;
while GetFromQueue(ResultStream) do begin
Client.Connection.IOHandler.Write(ResultStream);
ResultStream.Free;
end;
finally
Client.DataQueue.Free;
end;
end;
finally
Free;
end;
end;
procedure TServerForm.FormCreate(Sender: TObject);
begin
IdTCPServer1.Contexts.ListChanged := True;
IdTCPServer1.OnClientConnect := OnClientConnect;
IdTCPServer1.OnClientDisconnect := OnClientDisconnect;
end;
procedure TServerForm.OnClientConnect(AContext: Pointer);
var
Client: Pointer;
begin
Client := GetMemory(SizeOf(TClient));
with Client^ do begin
DataQueue := nil;
// Здесь может быть код для инициализации клиентской информации
end;
IdTCPServer1.Contexts.Add(Client);
end;
procedure TServerForm.OnClientDisconnect(AContext: Pointer; AConnection: Pointer);
var
Client: Pointer;
begin
Client := AContext;
if Assigned(Client^) then begin
if Assigned(Client^.DataQueue) then
Client^.DataQueue.Free;
SetLength(Client, 0);
IdTCPServer1.Contexts.Remove(AContext);
end;
end;
procedure TForm1.FormCreate(Sender: TObject);
begin
// Инициализация формы и сервера
end;
// Этот код представляет собой основу для обработки входящего потока данных и его последующего распространения на всех клиентов,
взаимодействие с приватными очередями для каждого клиента, что позволяет избежать блокировки основного потока чтения.
Заключение
Используя этот подход, вы можете реализовать одноканальный ввод потоковых данных от одного источника к множеству получателей без существенных задержек для главного потока чтения. Это эффективный метод работы с TCP-сервером и компонентами Indy в среде Delphi, который позволяет создавать многоточечные связные приложения.
Примечание: В примере выше используется механизм безопасных очередей для реализации параллельной передачи данных между клиентом-источником и множеством получателей. Это общий образец кода, как может быть организована такая система в вашем приложении на Delphi.
Важно убедиться, что все операции с данными выполняются асинхронно для избежания блокировки главного потока сервера.
В статье обсуждается метод обеспечения передачи данных от одного клиента ко многим в многосервисных веб-приложениях и играх, используя `Indy 10` в среде Delphi для реализации сервера с поддержкой одновременной передачи потоковых данных множеству подключе
Комментарии и вопросы
Получайте свежие новости и обновления по Object Pascal, Delphi и Lazarus прямо в свой смартфон. Подпишитесь на наш Telegram-канал delphi_kansoftware и будьте в курсе последних тенденций в разработке под Linux, Windows, Android и iOS
Материалы статей собраны из открытых источников, владелец сайта не претендует на авторство. Там где авторство установить не удалось, материал подаётся без имени автора. В случае если Вы считаете, что Ваши права нарушены, пожалуйста, свяжитесь с владельцем сайта.