TIdIOHandler.CheckForDataOnSource: ReadFromSource NoExceptionIfDisconnected (TIdTCPServer) #678
Replies: 2 comments 1 reply
-
Because, as its name implies, if not AContext.Connection.IOHandler.CheckForDataOnSource(2000) then
begin
AContext.Connection.IOHandler.CheckForDisconnect;
Continue; // or Exit, as needed...
end;
// process new data as needed...
You know how many bytes you are expecting, so you should just read the full packet header and packet data, and let the reads raise an exception if a disconnect or timeout occurs. For instance, check if the
The only way it could hog the CPU is if you are using a busy-wait until more bytes arrive, ie such as by calling
It would return False only if:
Calling
Correct.
The caller is explicitly asking for an exception not be raised. So a disconnect will not raise an exception. however, if the socket is still open then
The clean solution is to not use
One thing I notice is that you are calling You are also comparing Try something like this: type
TPacketHeader = record
x, y, cmd, z: UInt32;
end;
TPacket = record
header: TPacketHeader;
...
end;
function waitForPacketHeader(aIOHandler: TIdIOHandler; const aMaxTimeMs: LongInt): Boolean;
const
requiredNofBytes = SizeOf(TPacketHeader);
var
tStart: TIdTicks;
msElapsed: UInt32;
msLeft: Integer;
begin
Result := (aIOHandler.InputBuffer.Size >= requiredNofBytes);
if Result or (aMaxTimeMs <= 0) then begin
Exit;
end;
tStart := Ticks64();
msLeft := aMaxTimeMs;
repeat
if aIOHandler.CheckForDataOnSource(msLeft) then begin
Result := (aIOHandler.InputBuffer.Size >= requiredNofBytes);
if Result then begin
Exit;
end;
end else begin
aIOHandler.CheckForDisconnect;
end;
msElapsed := GetElapsedTicks(tStart);
if (msElapsed >= aMaxTimeMs) then begin
Exit;
end;
msLeft := aMaxTimeMs - msElapsed;
until (msLeft < 1);
end;
procedure readPacketHeader(aIOHandler: TIdIOHandler; var vPacket: TPacket);
var
buf: TIdBytes;
begin
aIOHandler.ReadBytes(buf, SizeOf(TPacketHeader), False);
Move(buf[0], vPacket.header, SizeOf(TPacketHeader));
// flip endian of header fields as needed...
{
alternatively:
vPacket.header.x := aIOHandler.ReadUInt32;
vPacket.header.y := aIOHandler.ReadUInt32;
vPacket.header.cmd := aIOHandler.ReadUInt32;
vPacket.header.z := aIOHandler.ReadUInt32;
}
end;
function readPacketBody(aIOHandler: TIdIOHandler; var vPacket: TPacket): Boolean;
begin
// use vPacket.header to read body as needed ...
end;
procedure TForm1.IdTCPServer1Execute(AContext: TIdContext);
var
rcvPacket: TPacket;
begin
//---1) READ AND PROCESS PACKET (if any received):
// wait 2" for a full packetheader.
// If a full packet header is available in the InputBuffer, call readPacketBody.
// Otherwise, proceed and send all packets from queue (if any)
if waitForPacketHeader(AContext.Connection.IOHandler, 2000) then begin
readPacketHeader(AContext.Connection.IOHandler, rcvPacket);
if (rcvPacket.header.cmd > 100) then begin
//log and discard rest of full packet
Exit;
end;
//---Read the rest of the packet
if readPacketBody(AContext.Connection.IOHandler, rcvPacket) then begin
//---Process Packet
processPacket(AContext, rcvPacket);
end else begin
//log
end;
end;
//---2) SEND ALL PACKETS
//while (lQueue.Count > 0) do sendPacket(...);
end; |
Beta Was this translation helpful? Give feedback.
-
|
Hello Remy,
The usage of calling IOHandler.CheckForDataOnSource() with a timeout has always been used and kept CPU usage really low, unless data needed to be read/written. I could rewrite some of the logic as you suggest (I think the conlusion is always reading all data from buffer as soon as there is data in the InputBuffer), but I would prefer to only touch the current logic if there is no other reliable way to detect that non respsonding/disconnected connection. So in fact only these question remain:
What happens:
So somewhere in the code, the disconnect seems detected, but the CheckForDisconnect() doesn't. Regards, |
Beta Was this translation helpful? Give feedback.
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
-
Hello everyone,
Why is ReadFromSource () inside TIdIOHandler.CheckForDataOnSource being called with ARaiseExceptionIfDisconnected=False?
I have a TIdTCPServer project where my loop looks like this:
However, I sometimes have the situation where a client connection sends an incomplete header (let's say, only 4 bytes of the 16) and then freezes, and this hogs the CPU. The reason seems to be because CheckForDataOnSource(2000) returns immediately (with False) if the client is disconnected but 4 bytes is inside the inputbuffer. I know/think Indy assumes that the connection is still connected when the InputBuffer is not empty. In this situation, ReadFromSource(False,..) is called, but it does not seem to try to read+add data to the buffer, hence does not detect the client connection is gone:
I don't really know how to solve this cleanly.
This is a very simplified demo of how my code looks:
Beta Was this translation helpful? Give feedback.
All reactions