Newsgroups : Borland : borland.public.delphi.internet.winsock : 2007 Nov : Re: tidTCPClient/Server - problem transfering data.
| Subject: | Re: tidTCPClient/Server - problem transfering data. |
| Posted by: | "Remy Lebeau (TeamB)" (no.spam@no.spam.com) |
| Date: | Thu, 22 Nov 2007 13:48:23 |
"Arvid Haugen" <Arvid@elis.no> wrote in message
news:474585b7@newsgroups.borland.com...
> HOWEVER - I still check the InputBufferIsEmpty - so I assume
> this is the problem after all.
The InputBuffer is only valid if you performed a reading operation that
filled it in. If you are using InputBufferIsEmpty to decide when to read,
then you are getting yourself into a catch-22 situation. 99.999999% of the
time, you do not need to rely on the contents of the InputBuffer at any
given time. Like I said earlier, it is for Indy's internal use. Just read
normally, and Indy will handle the rest for you.
> Do you think simply removing the check for
> ElWebServiceClient.IdTCPClient[indeks].IOHandler.InputBufferIsEmpty
> will solve my problem?
Yes. There is nothing in your code that should be using the InputBuffer
directly at all. Your logic is counter to how Indy is designed to work.
Once you finish reading all of the data from the InputBuffer, you are not
performing any further reads that will re-fill the InputBuffer. Just call
ReadLn() in the loop and forget the rest of the validation logic, it is not
necessary. For example:
while not lEndBitOK do
begin
/// Read data.
lData := ElWebServiceClient.IdTCPClient[indeks].IOHandler.ReadLn;
if Trim(lData) = cElwinMessageEndBit then
lEndBitOk := true
else
lParamListe.Add(lData);
//...
If you really want to use InputBufferIsEmpty(), then you need to use
CheckForDataOnSource() as well, ie:
while not lEndBitOK do
begin
/// Check that buffer is not empty.
if
ElWebServiceClient.IdTCPClient[indeks].IOHandler.InputBufferIsEmpty then
ElWebServiceClient.IdTCPClient[indeks].IOHandler.CheckForDataOnSource(10);
if not
ElWebServiceClient.IdTCPClient[indeks].IOHandler.InputBufferIsEmpty then
begin
lEbNo := 0;
/// Read data.
lData :=
ElWebServiceClient.IdTCPClient[indeks].IOHandler.ReadLn;
if Trim(lData) = cElwinMessageEndBit then
lEndBitOk := true
else
lParamListe.Add(lData);
end
else /// Else if input buffer empty and it has been the last 1000
reads (10 seconds) - quit...
if lEbNo > 1000 then
begin
logg('Can not find ENDBIT ('+cElwinMessageEndBit+') in message,
giving up...', clogError);
break;
end
else
begin
logg('Endbit not found, waiting for data to be written.',
clogError);
lEbNo := lEbNo+1;
Sleep(10);
Application.ProcessMessages;
end;
//...
> I tried to implement solution #1 that you outlined, but this makes
> my client hang
That is because you are not using it correctly.
> if I understand it correctly I will have to send the number of
> lines sendt from the server so that the client know how many
> lines to read
That is what setting the AWriteLinesCount parameter of Write(TString) does
for you automatically. By default, ReadStrings() expects that count to be
present in the socket data (if you specify a Count >=0 as a parameter to
ReadStrings(), it will not be read from the socket).
> I changed that code to something like this to prevent the client
> from hanging. Is this the correct way of doing it?
No. You are telling Write(TStrings) to send its own line count AND you are
sending your own manual line count as well (you are also subtracting 1 from
the count, which was wrong to do). In your client code, you are passing
your manual line count (which is 1 off) to ReadString(), which tells it to
not read the line count that Write(TStrings) sent. So if nothing else, your
first string is going to be corrupted because it has extra bytes in it.
If you want to send your own line count (which is perfectly valid to do if
you want to keep your protocol strictly textual - many protocols do), then
you need to set the AWriteLineCount parameter of Write(TSrings) back to
False, which you were originally using, ie:
--- server ---
procedure TForm5.IdTCPServer1Execute(AContext: TIdContext);
//...
begin
// ...
AContext.Connection.IOHandler.Write('201 ' +
IntToStr(LLines.Count));
AContext.Connection.IOHandler.Write(LLines{, False});
// ...
end;
--- client ---
var
LLines: TStrings;
LNumLines: Integer;
begin
Result := IdTCPClient1.SendCmd('100 Test', [201, 202]);
if Result = 201 then
begin
LNumLines := StrToInt(Trim(IdTCPClient1.LastCmdResult.Text[0]));
LLines := TStringList.Create;
try
IdTCPClient1.IOHandler.ReadStrings(LLines, LNumLines);
// loop through LLines as needed...
finally
LLines.Free;
end;
end;
else
//...
end;
Gambit