Re: ReadLn / WriteLn problem (Please ignore other post)

Giganews Newsgroups
Subject: Re: ReadLn / WriteLn problem (Please ignore other post)
Posted by:  Remy Lebeau (TeamB) (no.spam@no.spam.com)
Date: Fri, 9 Jun 2006

"m00bh000" <timkelly19…@hotmail.com> wrote in message
news:CA1C90EAB1FBE240timkelly19…@hotmail.com...

> I have an Indy TCP/IP server & client implementation, both
> with the following execute loops, which send and receive
> my custom packets:

Get rid of the loop altogether from the server's OnExecute code.  You should
not be looping at all.  The OnExecute event handler is already looped by
TIdTCPServer itself.  For example:

    procedure TForm1.IdTCPServer1Execute(AThread : TIDPeerThread);
    var
        myCustomPacket : string;
    begin
        myCustomPacket := AThread.Connection.ReadLn(LF, 100, -1);
        if myCustomPacket <> "" then ProcessPacket(myCustomPacket);
        myCustomPacket := MyThreadSafeStringQueue.Pop;
        if myCustomPacket <> "" then
AThread.Connection.WriteLn(myCustomPacket);
    end;

> Sometimes there are lots of packets to write and not many to be read.
> But the Write function is having to wait 100ms every time it wants to
write.

That is because you are only popping/writing one item at a time, doing a
blocking read in between.  You should be running through the entire queue
instead until it is empty.  For example, if you use TIdThreadSafeStringList
as your queue:

    procedure TForm1.IdTCPServer1Execute(AThread : TIDPeerThread);
    var
        s : string;
        Queue: TStringList;
        I: Integer;
    begin
        s := AThread.Connection.ReadLn(LF, 100, -1);
        if not AThread.Connection.ReadLnTimedOut then ProcessPacket(s);
        Queue := MyThreadSafeStringList.Lock;
        try
            while Queue.Count > 0 do
            begin
                AThread.Connection.WriteLn(Queue[0]);
                Queue.Delete(0);
            end;
        finally
            MyThreadSafeStringList.Unlock;
        end;
    end;

> I don't want to set the ReadLn timeout to 1ms because I think this will
use too much CPU time.

You could use the Readable() method to wait for data to arrive before
actually trying to read it, ie:

    procedure TForm1.IdTCPServer1Execute(AThread : TIDPeerThread);
    var
        s : string;
    begin
        if AThread.Connection.InputBuffer.Size = 0 then
AThread.Connection.ReadFromStack(True, 1, False);
        if AThread.Connection.InputBuffer.Size > 0 then
ProcessPacket(AThread.Connection.ReadLn);
        // process write queue ...
    end;

> I wasn't sure if you can saftly ReadLn and WriteLn from different
> threads at the same time

You can.  A socket maintains separate buffers for reading andf writing.  The
only danger is having two threads both read, or both write, to the socket at
the same time.  You have to implement your own synchronizing or locking
mechanism to prevent that.

> Incidently MyThreadSafeStringQueue does have a TSimpleEvent
> which is signalled if there is data waiting to be sent.

Ok, then you don't have to access the contents of the queue at all if the
event has not been signaled yet.

>  WaitForMultipleEvents(Connection.DataWaitingEvent,
SentQueue.DataWaitingEvent)

Since Indy uses WinSock 2.0, the socket handle itself can be used in such a
wait on Windows systems.  You can access the socket handle via the
AThread.Connection.Socket.Binding.Handle property.

Just keep in mind, though, that when Indy reads data from the socket, it
reads EVERYTHING that is currently available, even if the reading method
does not return it all.  Any pending data remains in the InputBuffer, so
make sure that you are waiting on the socket handle only if the InputBuffer
is empty, or does not contain a complete line yet.

Gambit

Replies

None

In response to

ReadLn / WriteLn problem (Please ignore other post) posted by m00bh000 on Fri, 9 Jun 2006