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

Оптимизация работы с пакетами: как избежать потерь в клиентском TCP-потоке на Delphi

Delphi , Интернет и Сети , TCP/IP

В статье мы рассмотрим проблему пропуска пакетов при одновременном их поступлении от одного источника, с использованием библиотеки Indy в среде разработки Delphi. Проблема заключается в том, что при большом количестве одновременно прибывающих пакетов клиентский поток не успевает их обработать, и происходит потеря данных.

Основные моменты статьи:

  1. Описание проблемы: В коде клиента используется интервал в 10 мс для обработки пакетов, но при одновременном поступлении многих пакетов от одного источника возникают пропуски.
  2. Текущий подход к чтению данных: Используется проверка наличия данных на источнике и последующее извлечение байтов из буфера ввода.
  3. Альтернативное решение: Предоставляется код, демонстрирующий более корректный способ обработки пакетов с использованием функций чтения Indy.

Шаги к решению:

  1. Проверьте размер буфера ввода перед извлечением данных, чтобы избежать исключений при нехватке байтов.
  2. Очистите буфер от предыдущих данных перед каждым новым чтением, чтобы предотвратить коррупцию данных.

Подробное решение:

Для оптимизации работы с пакетами и предотвращения их потерь необходимо использовать встроенные функции Indy для чтения данных. Вместо прямого доступа к буферу ввода следует воспользоваться ReadLongInt для получения размера сообщения, а затем ReadBytes для извлечения самого сообщения.

procedure TForm1.THEX_TCP;
var
  Buffer: TBytes;
  MsgSize: Integer;
begin
  MsgSize := TCPClient.IOHandler.ReadLongInt; // Чтение размера сообщения
  TCPClient.IOHandler.ReadBytes(Buffer, MsgSize); // Чтение содержимого сообщения
  Inc(fRXCount);
  NAT.RecievedNATData(Buffer); // Обработка полученного пакета
end;

Этот подход позволяет Indy самостоятельно управлять буфером ввода и корректно обрабатывать поступающие данные.

Для ситуаций, когда необходимо прервать обработку при отсутствии данных, можно использовать следующий код:

procedure TForm1.THEX_TCP;
var
  Buffer: TBytes;
  MsgSize: Integer;
begin
  if TCPClient.IOHandler.InputBufferIsEmpty then // Проверка на наличие данных в буфере
  begin
    TCPClient.IOHandler.CheckForDataOnSource; // Проверка поступления новых данных
    TCPClient.IOHandler.CheckForDisconnect; // Проверка на отключение клиента
    if TCPClient.IOHandler.InputBufferIsEmpty then Exit; // Выход, если данные отсутствуют
  end;
  repeat // Цикл обработки пакетов до пустого буфера
  begin
    MsgSize := TCPClient.IOHandler.ReadLongInt;
    TCPClient.IOHandler.ReadBytes(Buffer, MsgSize);
    Inc(fRXCount);
    NAT.RecievedNATData(Buffer);
    SetLength(Buffer, 0); // Очистка буфера перед следующим чтением
  end
  until TCPClient.IOHandler.InputBufferIsEmpty;
end;

Важно отметить, что при использовании цикла ReadLongInt и ReadBytes может произойти загрузка большего количества данных в буфер, чем требуется для обработки одного сообщения. В таком случае следует использовать модифицированный подход:

procedure TForm1.THEX_TCP;
var
  MsgSizeBuffer: array[0..3] of Byte;
  MsgSize, I: Integer;
  Buffer: TBytes;
begin
  TCPClient.IOHandler.CheckForDataOnSource; // Проверка на поступление новых данных
  TCPClient.IOHandler.CheckForDisconnect; // Проверка на отключение клиента
  while TCPClient.IOHandler.InputBuffer.Size >= 4 do // Обработка пакетов до заполнения буфера размером сообщения
  begin
    for I := 0 to 3 do // Чтение размера сообщения без удаления из буфера
      MsgSizeBuffer[I] := TCPClient.IOHandler.InputBuffer.Peek(I);
    Move(MsgSizeBuffer[0], MsgSize, 4); // Преобразование байтов в размер сообщения
    MsgSize := LongInt(GStack.NetworkToHost(LongWord(MsgSize))); // Приведение к сетевому порядку байт
    if TCPClient.IOHandler.InputBuffer.Size < (4+MsgSize) then Break; // Проверка на достаточный объем данных в буфере
    TCPClient.IOHandler.InputBuffer.Remove(4); // Удаление размера сообщения из буфера
    TCPClient.IOHandler.InputBuffer.ExtractToBytes(Buffer, MsgSize); // Извлечение содержимого сообщения
    Inc(fRXCount);
    NAT.RecievedNATData(Buffer);
    SetLength(Buffer, 0); // Очистка буфера перед следующим чтением
  end;
end;

В данном примере используется функция Peek для извлечения размера пакета без его удаления из буфера, что позволяет обрабатывать только полные сообщения.

Выводы:

Использование встроенных функций Indy для чтения данных позволит избежать потерь пакетов и упростит обработку поступающих сообщений. При правильной настройке кода клиента, основанного на использовании этих функций, можно достичь нулевой потери пакетов в TCP-потоке.

Комментарии:

Не забудьте, что ReadLongInt и другие функции чтения Indy по умолчанию используют преобразование сетевого порядка байтов. Убедитесь, что ваш протокол следует этому правилу при отправке целочисленных значений.

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

Проблема пропуска пакетов в клиентском потоке решается оптимизацией чтения данных с использованием функций библиотеки Indy для предотвращения потерь при одновременном поступлении множества пакетов.


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

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




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


:: Главная :: TCP/IP ::


реклама


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

Время компиляции файла: 2024-12-22 20:14:06
2025-03-12 07:20:29/0.0036108493804932/0