The problem is in your code, not in ReadStream()
. It is acting as designed.
It accepts 3 parameters for input:
procedure ReadStream(AStream: TStream; AByteCount: TIdStreamSize = -1; AReadUntilDisconnect: Boolean = False); virtual;
You are only providing a value for the first parameter, so the other two parameters use default values.
When the AByteCount
parameter is set to -1 and the AReadUntilDisconnect
parameter is set to False, ReadStream()
is designed to assume that the first 4 bytes received (or 8 bytes, if the IOHandler.LargeStream
property is set to True) are the length of the data being sent, followed by the actual data afterwards. That is why ReadStream()
is calling ReadLongInt()
. Not only does this tell ReadStream()
when to stop reading, but it also allows ReadStream()
to pre-size the target TStream for better memory management before receiving the data.
If the client is not actually sending a 4-byte (or 8-byte) length value ahead of its data, then ReadStream()
will still interpret the beginning bytes of the real data as a length. This typically (but not always, depending on the data) results in ReadLongInt()
(or ReadInt64()
) returning a large integer value, which would then cause ReadStream()
to expect a huge amount of data that will never actually arrive, thus blocking the reading indefinately (or until a timeout occurs, if the IOHandler.ReadTimeout
property is set to a non-infinite timeout).
In order to use ReadStream()
effectively, it needs to know when to stop reading, either by being told how much data to expect ahead of time (ie: AByteCount >= 0
), or by requiring the sender to disconnect after sending its data (ie: AReadUtilDisconnect = True
). The combination of AByteCount = -1
and AReadUtilDisconnect = False
is a special case, when the length is encoded directly in the streaming. This is primarily used (but not limited to) when the sender calls IOHandler.Write(TStream)
with its AWriteByteCount
parameter set to True (it is False by default).
When dealing with non-textual data, it is always a good idea to send the data length ahead of the actual data whenever possible. It optimizes reading operations.
The different parameter combinations of ReadStream() work out to the following logic:
AByteCount = -1, AReadUtilDisconnect = False: read 4/8 bytes, interpret as a length, then keep reading until that length is received.
AByteCount < -1, AReadUtilDisconnect = False: assume AReadUntilDisconnect is True and keep reading until disconnected.
AByteCount > -1, AReadUtilDisconnect = False: pre-size the target TStream and keep reading until AByteCount number of bytes are received.
AByteCount <= -1, AReadUtilDisconnect = True: keep reading until disconnected.
AByteCount > -1, AReadUtilDisconnect = True: pre-size the target TStream and keep reading until disconnected.
Depending on the kind of data the client is actually sending to the server in the first place, chances are that ReadStream()
is likely not the best choice for reading that data. The IOHandler has many different kinds of reading methods available. For instance, if the client is sending delimited text (especially if it is being sent with IOHandler.WriteLn()
), then ReadLn()
is a better choice.