Карта сайта Kansoftware
НОВОСТИУСЛУГИРЕШЕНИЯКОНТАКТЫ
KANSoftWare

Использование ICS для передачи данных в виде TBytes между сервером и клиентом в Delphi и Pascal

Delphi , Интернет и Сети , XML

Передача бинарных данных с использованием ICS в Delphi: решение для TCP/IP сервера и клиентов

В этой статье мы рассмотрим, как реализовать надежную передачу бинарных данных между сервером и несколькими клиентами с использованием библиотеки ICS (Internet Component Suite) в Delphi, с акцентом на работу с типом TBytes и кастомными разделителями сообщений.

Постановка задачи

Как видно из обсуждения на форуме, пользователь AJ_Oldendorf столкнулся с необходимостью:

  • Создать TCP/IP сервер и клиентские приложения (до 15 клиентов)
  • Передавать данные в виде TBytes (от 20 до 60000 байт)
  • Использовать фиксированный 8-байтовый маркер конца сообщения (EndOfLine)
  • Обеспечить высокую скорость передачи (80-90 сообщений/сек от сервера, 20 сообщений/сек от клиентов)
  • Обрабатывать данные в фоновом потоке, не блокируя GUI

Решение с использованием ICS

Выбор компонентов

Как правильно отметил Angus Robertson, для этой задачи подходят несколько компонентов из состава ICS:

  1. TIcsIpStrmLog - удобен для простой передачи данных, но не поддерживает длинные разделители
  2. TSslWSocketServer/TTcpSrvClient - более низкоуровневые компоненты, позволяющие реализовать кастомную логику

Мы сосредоточимся на втором варианте, так как он предоставляет больше гибкости.

Реализация сервера

type
  TTcpSrvClient = class(TWSocketClient)
  private
    FBuffer: TBytes;
    FEOLMarker: TBytes;
  public
    constructor Create(AOwner: TComponent); override;
    procedure ProcessData(const Data: TBytes);
  end;

constructor TTcpSrvClient.Create(AOwner: TComponent);
begin
  inherited;
  // Инициализируем 8-байтовый маркер конца сообщения
  SetLength(FEOLMarker, 8);
  FEOLMarker[0] := 65; // 'A'
  FEOLMarker[1] := 65; // 'A'
  FEOLMarker[2] := 66; // 'B'
  FEOLMarker[3] := 66; // 'B'
  FEOLMarker[4] := 67; // 'C'
  FEOLMarker[5] := 67; // 'C'
  FEOLMarker[6] := 68; // 'D'
  FEOLMarker[7] := 68; // 'D'

  SetLength(FBuffer, 0);
end;

procedure TTcpSrvClient.ProcessData(const Data: TBytes);
begin
  // Здесь обрабатываем полное сообщение
  // Можно передать в основной поток через Synchronize или Queue
end;

procedure TForm1.ClientDataAvailable(Sender: TObject; ErrCode: Word);
var
  Client: TTcpSrvClient;
  NewData, TempBuffer: TBytes;
  I, J, EOLPos: Integer;
  Found: Boolean;
begin
  Client := Sender as TTcpSrvClient;

  // Получаем новые данные
  NewData := Client.ReceiveBytes;

  // Добавляем к буферу
  TempBuffer := Client.FBuffer;
  SetLength(TempBuffer, Length(TempBuffer) + Length(NewData));
  Move(NewData[0], TempBuffer[Length(TempBuffer) - Length(NewData)], Length(NewData));

  // Ищем маркер конца сообщения
  EOLPos := -1;
  for I := 0 to Length(TempBuffer) - Length(Client.FEOLMarker) do
  begin
    Found := True;
    for J := 0 to Length(Client.FEOLMarker) - 1 do
    begin
      if TempBuffer[I + J] <> Client.FEOLMarker[J] then
      begin
        Found := False;
        Break;
      end;
    end;

    if Found then
    begin
      EOLPos := I;
      Break;
    end;
  end;

  if EOLPos >= 0 then
  begin
    // Извлекаем полное сообщение
    SetLength(NewData, EOLPos);
    Move(TempBuffer[0], NewData[0], EOLPos);

    // Обрабатываем сообщение
    TThread.Queue(nil, 
      procedure
      begin
        Client.ProcessData(NewData);
      end);

    // Сохраняем оставшиеся данные
    SetLength(Client.FBuffer, Length(TempBuffer) - EOLPos - Length(Client.FEOLMarker));
    if Length(Client.FBuffer) > 0 then
      Move(TempBuffer[EOLPos + Length(Client.FEOLMarker)], Client.FBuffer[0], Length(Client.FBuffer));
  end
  else
  begin
    // Сохраняем данные в буфер для следующего пакета
    Client.FBuffer := TempBuffer;
  end;
end;

Настройка сервера

procedure TForm1.StartButtonClick(Sender: TObject);
begin
  SslWSocketServer1.Proto := 'tcp';
  SslWSocketServer1.Addr := '0.0.0.0';
  SslWSocketServer1.Port := '8080';
  SslWSocketServer1.SslEnable := False;
  SslWSocketServer1.ClientClass := TTcpSrvClient;
  SslWSocketServer1.OnClientConnect := ClientConnect;
  SslWSocketServer1.Listen;
end;

procedure TForm1.ClientConnect(Sender: TObject; Client: TWSocketClient; Error: Word);
begin
  with Client as TTcpSrvClient do
  begin
    OnDataAvailable := ClientDataAvailable;
    OnBgException := ClientBgException;
  end;
end;

Реализация клиента

Клиентская часть реализуется аналогично, с использованием TWSocket:

procedure TClientForm.SendData(const Data: TBytes);
var
  FullMessage: TBytes;
begin
  // Добавляем маркер конца сообщения
  SetLength(FullMessage, Length(Data) + Length(FEOLMarker));
  Move(Data[0], FullMessage[0], Length(Data));
  Move(FEOLMarker[0], FullMessage[Length(Data)], Length(FEOLMarker));

  WSocket.Send(FullMessage, Length(FullMessage));
end;

procedure TClientForm.WSocketDataAvailable(Sender: TObject; ErrCode: Word);
begin
  // Аналогичная серверу обработка входящих данных
end;

Альтернативное решение

Если реализация с кастомным поиском маркера кажется слишком сложной, можно рассмотреть альтернативный подход:

  1. Передача длины сообщения - в начале каждого сообщения отправлять 4 байта с длиной данных
  2. Использование TIcsBufferedStream - для упрощения работы с буферизацией

Пример альтернативной реализации:

procedure TForm1.AlternativeClientDataAvailable(Sender: TObject; ErrCode: Word);
var
  Client: TTcpSrvClient;
  NewData: TBytes;
  DataSize: Integer;
begin
  Client := Sender as TTcpSrvClient;

  // Читаем доступные данные
  NewData := Client.ReceiveBytes;

  // Добавляем в буфер
  Client.FBuffer := Client.FBuffer + NewData;

  // Пока в буфере достаточно данных для чтения размера
  while Length(Client.FBuffer) >= 4 do
  begin
    // Читаем размер данных
    Move(Client.FBuffer[0], DataSize, 4);

    // Проверяем, есть ли полное сообщение
    if Length(Client.FBuffer) >= 4 + DataSize then
    begin
      // Извлекаем данные
      SetLength(NewData, DataSize);
      Move(Client.FBuffer[4], NewData[0], DataSize);

      // Обрабатываем сообщение
      Client.ProcessData(NewData);

      // Удаляем обработанные данные из буфера
      Delete(Client.FBuffer, 0, 4 + DataSize);
    end
    else
      Break;
  end;
end;

Рекомендации по производительности

  1. Размер буфера - для высоких нагрузок увеличьте размер буфера (по умолчанию 8КБ):
    WSocket.BufSize := 65536; // 64KB

  2. Многопоточность - обработку данных лучше выносить в отдельные потоки:
    TThread.CreateAnonymousThread( procedure begin ProcessDataIntensive(Data); end).Start;

  3. Ограничение скорости - при необходимости можно ограничить скорость передачи:
    WSocket.SendFlags := WSocket.SendFlags + [wsSendThrottle];
    WSocket.Throughput := 102400; // 100KB/s

Заключение

Библиотека ICS предоставляет гибкие инструменты для реализации TCP/IP серверов и клиентов в Delphi. Для работы с бинарными данными и кастомными разделителями сообщений лучше использовать низкоуровневые компоненты, такие как TWSocketServer и TWSocketClient, реализуя собственную логику буферизации и обработки данных.

Представленные решения позволяют:
- Надежно передавать бинарные данные любого размера
- Использовать кастомные маркеры конца сообщения
- Обрабатывать данные в фоновых потоках
- Масштабироваться на множество клиентских подключений

Для упрощения кода можно рассмотреть альтернативный подход с передачей длины сообщения, который может быть более эффективным для некоторых сценариев.

Создано по материалам из источника по ссылке.

Реализация передачи бинарных данных между сервером и клиентами в Delphi с использованием библиотеки ICS, включая обработку кастомных маркеров и фоновую обработку данных.


Комментарии и вопросы

Получайте свежие новости и обновления по Object Pascal, Delphi и Lazarus прямо в свой смартфон. Подпишитесь на наш Telegram-канал delphi_kansoftware и будьте в курсе последних тенденций в разработке под Linux, Windows, Android и iOS




Материалы статей собраны из открытых источников, владелец сайта не претендует на авторство. Там где авторство установить не удалось, материал подаётся без имени автора. В случае если Вы считаете, что Ваши права нарушены, пожалуйста, свяжитесь с владельцем сайта.


:: Главная :: XML ::


реклама


©KANSoftWare (разработка программного обеспечения, создание программ, создание интерактивных сайтов), 2007
Top.Mail.Ru

Время компиляции файла: 2024-12-22 20:14:06
2025-08-27 09:33:42/0.0041189193725586/0