В статье мы рассмотрим проблему пропуска пакетов при одновременном их поступлении от одного источника, с использованием библиотеки Indy в среде разработки Delphi. Проблема заключается в том, что при большом количестве одновременно прибывающих пакетов клиентский поток не успевает их обработать, и происходит потеря данных.
Основные моменты статьи:
Описание проблемы: В коде клиента используется интервал в 10 мс для обработки пакетов, но при одновременном поступлении многих пакетов от одного источника возникают пропуски.
Текущий подход к чтению данных: Используется проверка наличия данных на источнике и последующее извлечение байтов из буфера ввода.
Альтернативное решение: Предоставляется код, демонстрирующий более корректный способ обработки пакетов с использованием функций чтения Indy.
Шаги к решению:
Проверьте размер буфера ввода перед извлечением данных, чтобы избежать исключений при нехватке байтов.
Очистите буфер от предыдущих данных перед каждым новым чтением, чтобы предотвратить коррупцию данных.
Подробное решение:
Для оптимизации работы с пакетами и предотвращения их потерь необходимо использовать встроенные функции 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
Материалы статей собраны из открытых источников, владелец сайта не претендует на авторство. Там где авторство установить не удалось, материал подаётся без имени автора. В случае если Вы считаете, что Ваши права нарушены, пожалуйста, свяжитесь с владельцем сайта.