Newsgroups : Borland : borland.public.delphi.internet.winsock : 2008 Feb : Re: Indy 9 TidTCPClient.WriteStream problem
| Subject: | Re: Indy 9 TidTCPClient.WriteStream problem |
| Posted by: | "Tomislav Stamac" (tstam..@vip.hr) |
| Date: | Thu, 28 Feb 2008 09:17:21 |
Thanks for reply Remy.
>
> The only way that can happen is if the TStream is empty to begin with.
> The OnWorkBegin event should always be getting triggered regardless,
> specifying a value in its AWorkCountMax parameter, even if it is zero.
>
1. Stream is not Empty.
2. OnWorkBegin does get triggered, OnWorkEnd also.
The problem is that OnWork does not get triggered.
3. Debugging Indy source:
procedure TIdTCPConnection.WriteStream(AStream: TStream; const AAll: boolean
= true;
const AWriteByteCount: Boolean = False; const ASize: Integer = 0);
var
LBuffer: TMemoryStream;
LSize: Integer;
LStreamEnd: Integer;
LBufferingStarted: Boolean;
begin
if AAll then begin
AStream.Position := 0;
end;
// This is copied to a local var because accessing .Size is very
inefficient
if ASize = 0 then begin
LStreamEnd := AStream.Size;
end else begin
LStreamEnd := ASize + AStream.Position;
end;
LSize := LStreamEnd - AStream.Position;
LBufferingStarted := FWriteBuffer = nil;
if LBufferingStarted then
begin
OpenWriteBuffer;
end;
try
if AWriteByteCount then begin
WriteInteger(LSize);
end;
BeginWork(wmWrite, LSize); try
LBuffer := TMemoryStream.Create; try
LBuffer.SetSize(FSendBufferSize);
while True do begin
LSize := Min(LStreamEnd - AStream.Position, FSendBufferSize);
if LSize = 0 then begin
Break;
end;
// Do not use ReadBuffer. Some source streams are real time and
will not
// return as much data as we request. Kind of like recv()
// NOTE: We use .Size - size must be supported even if real time
LSize := AStream.Read(LBuffer.Memory^, LSize);
<---- INDY DOES READ CHUNKS OF STREAM IN INTERNAL
if LSize = 0 then begin
BUFFER
raise EIdNoDataToRead.Create(RSIdNoDataToRead);
end;
WriteBuffer(LBuffer.Memory^, LSize);
<---- THEN IT CALLS WRITEBUFFER WITH WRITENOW=FALSE
end;
finally FreeAndNil(LBuffer); end;
finally EndWork(wmWrite); end;
if LBufferingStarted then
begin
CloseWriteBuffer;
end;
.......
WRITEBUFFER IS CALLED WITH WRITENOW=FALSE
procedure TIdTCPConnection.WriteBuffer(const ABuffer; AByteCount: Integer;
const AWriteNow: boolean = false);
var
LBuffer: TIdSimpleBuffer;
nPos, nByteCount: Integer;
begin
if (AByteCount > 0) and (@ABuffer <> nil) then begin
// Check if disconnected
CheckForDisconnect(True, True);
if connected then begin
if (FWriteBuffer = nil) or AWriteNow then begin <---------
THIS IS FALSE SO WE SKEEP TO ELSE
LBuffer := TIdSimpleBuffer.Create; try
LBuffer.WriteBuffer(ABuffer, AByteCount);
if Assigned(Intercept) then begin
LBuffer.Position := 0;
Intercept.Send(LBuffer);
AByteCount := LBuffer.Size;
end;
nPos := 1;
repeat
nByteCount := IOHandler.Send(PChar(LBuffer.Memory)[nPos - 1],
LBuffer.Size - nPos + 1);
FClosedGracefully := nByteCount = 0;
// Check if other side disconnected
CheckForDisconnect;
// Check to see if the error signifies disconnection
if GStack.CheckForSocketError(nByteCount, [ID_WSAESHUTDOWN,
Id_WSAECONNABORTED, Id_WSAECONNRESET]) then begin
DisconnectSocket;
GStack.RaiseSocketError(GStack.LastError);
end;
// TODO - Have a AntiFreeze param which allows the send to be
split up so that process
// can be called more. Maybe a prop of the connection,
MaxSendSize?
TIdAntiFreezeBase.DoProcess(False);
DoWork(wmWrite, nByteCount); <---- DO WORK
WOULD BE CALLED IF WE PASSED THROUGH THIS BLOCK
nPos := nPos + nByteCount;
until nPos > AByteCount;
finally FreeAndNil(LBuffer); end;
// Write Buffering is enabled
end else begin
<--- SINCE CONDITION WAS FALSE WE ARE GOING THROUGH THIS BLOCK
FWriteBuffer.WriteBuffer(ABuffer, AByteCount); AND THERE IS
NO DOWORK HERE
if (FWriteBuffer.Size >= FWriteBufferThreshhold) and
(FWriteBufferThreshhold > 0) then begin
// TODO: Maybe? instead of flushing - Write until buffer is
smaller than Threshold.
// That is do at least one physical send.
FlushWriteBuffer(FWriteBufferThreshhold);
end;
end;
end
else
begin
raise EIdNotConnected.Create(RSNotConnected);
end;
end;
end;
>> In Indy 8 I used tprogresstream for showing progressbar
>> where tprogressstream had OnRead(Bytesread:cardinal);
>> method. In Indy 9 I cant use it
>
> Yes, you can.
>
>> because Indy 9 seems to read all stream in the internal buffer
>> or something and after that it sends stream,
>
> No, that is not what Indy 9 does.
>
>
> Gambit
>
Oh but it does, :-) as You can see in WriteStream the buffer is callled
LBuffer and itself it is a TMemoryStream.
The problem is that it copies chunks of original stream in that buffer and
sends them with writebuffer(..,..,false)... ofcourse that lasts 10ms, and my
progressbar is at 100% at that point, but sending over socket is still going
on and it lasts around 45s over GPRS or about 5s over HSDPA.
Am I missing something here?
I suppose that I can copy internal Indy behaviour and send stream like this:
Size:=MyProgressStream.Size;
MyProgressStream.OnRead:=DoProgress;
Client.WriteInteger(Size);
Client.WriteBuffer(MyProgressStream.Memory^,size,true);
Bit It's ugly :), there is Client.WriteStream to do that job.