Re: Client Server application using Indy

Giganews Newsgroups
Subject: Re: Client Server application using Indy
Posted by:  Remy Lebeau (Indy Team) (no.spam@no.spam.com)
Date: Mon, 14 Aug 2006

"Martin" <mailoptim…@yahoo.com> wrote in message
news:8B832349F903E340mailoptim…@yahoo.com...

> I know that the TIdAntiFreeze component affects performance
> although it allows my clients to be more responsive. Does anyone
> know the performance penalty?

That depends on how much GUI operations you are performing.  TIdAntiFreeze
effectively boils down to a series of calls to Application.ProcessMessages()
during blocking socket operations.  If you do a lot of message-based
operations, or have event handlers that are not protected from re-entrant
problems, then the extra message processing can cause problems if you are
not careful.

> Do I need it also in the Server application?

TIdAntiFreeze only effects Indy components that perform reading/writing
operations in the context of the main thread.  Indy servers are
multi-threaded, and thus do not perform any reading/writing operations in
the main thread by default.  You would have to force them by
Synchronize()'ing with the main thread.

> Do I need to use TIdThreadMgrDefault in my Server application?

TIdTCPServer already uses that component by default.  If you do not assign a
ThreadMgr, one is assigned internally by Indy automatically when the server
is activated.  You do not need to assign a ThreadMgr unless you want to
control the thread management yourself, such as enabling the use of thread
pooling.

> In my previous program I could send records of different sizes or types,
> from either the Server or Client side (either direction) without a problem
> since I could use the TClientSocket / TServerSocket Read events and the
> ReceiveLength property to know when and the size (differentiate) of the
> records.

That was a very bad design on your part.  It is very error prone to rely on
ReceiveLength to handle entire packets on a per-event basis.  There are 4
different scenerios that can occur when the OnRead event is triggered:

1) the event is triggered too many times due to cached events for data that
was received and processed from earlier events.  This causes ReceiveLength()
to sometimes return 0.

2) ReceiveLength() returns a value indicating the full size of one single
complete packet that can be processed as-is.  This is the only scenerio you
have described above.

3) ReceiveLength() returns a value indicating the size of one single partial
packet.

4) ReceiveLength() returns a value indicating the size of more than one
partial and/or complete packets that have been concatentated together.

Remember that TCP is an arbitrary byte stream.  It has no concept of where
one logical record ends and the next begins.  You have to manage that in
your own code.  The best way to handle all of the above scenerios in an
effective manner is to buffer all inbound data somewhere and check the
buffer for complete packets in each OnRead event that is triggered,
processing the complete packets as needed, and then leaving any remaining
partial packets in the buffer for later events to finish filling in.  Go to
http://www.deja.com and search through the newsgroup archives, as sample
code has been posted many many times before to demonstrate this.

> Is it possible to send records of different sizes or types using Indy?

Yes.  In fact, Indy handles all of the above scenerios internally for you.
All you have to do is tell Indy how many bytes to read, and it does all of
the buffering and waiting internally as needed.

Whether you use Indy or not, the best way to ensure that you can
differentiate where records are separated from each other is to either:

1) prefix each variable-length record with a fixed-length value indicating
its size.  The receiver can then read the length first, and then wait for
the specified number of bytes to arrive afterwards.

2) put a unique delimiter byte sequence in between each record, one that is
guaranteed never to appear in your record data.  The receiver can then
continuous read from the connection until the sequence is received.

I prefer #1 whenever possible.  It is much more efficient, not only on the
processing of packets, but on memory management and such as well.

> I have used a Timer component with an interval of fifteen (15) seconds
> on the Client side to check for Server commands based on the ScreenThief
> project from the website ? Delphi.About, but I was wondering if there was
> a better way of determining when the Server sends commands.

Indy uses blocking sockets exclusively, which do not provide any such
notifications.  Simply read from the connection and let it block until data
arrives.  Avoid making blocking calls in the context of the main thread
whenever possible.  Use reading threads instead.  If you must perform
reading operations in the main thread, then you can at least call Indy's
Readable() method beforehand with a short timeout interval.  Unless you
don't care if your UI becomes blocked and unresponsie while waiting.

> Considering the number of Clients, wont the traffic load on the Network
> be high when using the OnTimer method of querying the Server ?

No, because such queries do not go over the network to begin with.  Checking
a connection for available data in the socket's buffer is a local operation.

> I would like to handle abnormal disconnections of the Clients or Server
> gracefully.

Blocking sockets do not natively offer that capability.  A socket cannot
detect an abnormal disconnect in a timely manner.  The socket has to timeout
internally first, and unfortunately the default timeout is very length
(upwards of several hours by default).  The best way to handle this when
using blocking sockets is to implement your own keepalive mechanism in your
data protocol.

Have your clients send keepalive packets periodically, and have the server
respond accordingly.  If the server does not receive any data from a
particular client after a period of time, assume the connection has been
lost regardless of its actual status and close just that connection on the
server side.  If the client has not disconnected abnormally, but it is just
running too slow, then this will eventually show up as a graceful disconnect
that the client will have to handle on its own side.

This also allows clients to detect when the server is no longer responding
so that they can disconnect and then reconnect as needed.

> These could be due to a computer freezing/crashing, terminating either
> the Server or Client application through Task manager or a computer
> being switched off accidentally e.g. turning off the wall switch or UPS.

If you are shutting down gracefully, such as from a normal application
shutdown, then it is always nice to send a goodbye packet to the server just
before disconnecting.  This way, the server knows the client is leaving
intentionally.  You can then treat unannounced graceful disconnects as
abnormal disconnects if you want to (such Internet protocols, like SMTP for
example, require this).

> After studying the SendReceiveRecords Indy demo, I learned that I can
> send records using Streams. Where can I get a good example of sending
> records using streams?

Using streams does not change anything I have said above.  In fact, Indy's
stream operations can employ packet management scenerio #1 that I describe
above, by sending the stream's length before then sending the stream's
actual data.  This allows Indy to pre-allocate the target stream on the
receiver's end.  Of course, this is not strictly a requirement, but it does
help onsiderable whenever it can be used.

Gambit

Replies

In response to

Client Server application using Indy posted by Martin on Mon, 14 Aug 2006