Пользовательский обработчик IOHandler
Работа Speed Debugger делается на основе пользовательского IOHandler. Маппированый порт компонент имеет событие OnConnect, и данное событие используется как хук в нашем IOHandler для каждого исходящего клиента, который создает маппированый порт. Это выглядит так:
procedure TformMain.IdMappedPortTCP1Connect(AThread: TIdMappedPortThread);
var
LClient: TIdTCPConnection;
LDebugger: TMyDebugger;
begin
LClient := AThread.OutboundClient;
LDebugger := TMyDebugger.Create(LClient);
LDebugger.BytesPerSecond := GSpeed;
LClient.IOHandler := LDebugger;
end;
Пользовательский класс IOHandler, называется TMyDebugger и реализован как наследник от TIdIOHandlerSocket, являющегося наследником IOHandler. Поскольку TIdIOHandlerSocket уже реализует весь актуальный ввод/вывод, TMyDebugger должен только замедлить передачу данных. Это делается путем перекрытия метода Recv.
Из метода Recv вызывается наследованный Recv для приема данных. Затем, базируясь на выбранном ограничении скорости, рассчитывается необходимая задержка. Если рассчитанная величина больше, чем наследованная, то метод Recv вызывает Sleep. Это может казаться сложным, но на самом деле это очень просто. Метод Recv приведен ниже.
function TMyDebugger.Recv(var ABuf; ALen: integer): integer;
var
LWaitTime: Cardinal;
LRecvTime: Cardinal;
begin
if FBytesPerSecond > 0 then
begin
LRecvTime := IdGlobal.GetTickCount;
Result := inherited Recv(ABuf, ALen);
LRecvTime := GetTickDiff(LRecvTime, IdGlobal.GetTickCount);
LWaitTime := (Result * 1000) div FBytesPerSecond;
if LWaitTime > LRecvTime then
begin
IdGlobal.Sleep(LWaitTime – LRecvTime);
end;
end
else
begin
Result := inherited Recv(ABuf, ALen);
end;
end;