🚀 go-pugleaf

RetroBBS NetNews Server

Inspired by RockSolid Light RIP Retro Guy

Article View: borland.public.cppbuilder.internet.socket
Article #1430

Re: Indy TCPServer, client disconnects not detected

#1430
From: Remy Lebeau (TeamB)
Date: Tue, 16 Jan 2007 09:15
146 lines
6628 bytes
"Dennis Jones" <nospam@nospam.com> wrote in message
news:45a86ffe$1@newsgroups.borland.com...

> I am now getting a report of my application basically hanging, or
utilizing
> too much CPU%, at a brand new site that is testing my app for use in
> their environment.

Then you are doing something wrong in your server code.  Even if a
client is lost abnormally, your code should not be hitting the CPU
that hard.  If it is, then you are not protecting your code properly.

> In looking at my log files, I see incoming connections from a client
> application, which sends a few messages and then...does nothing
> -- though according to the customer, the client app disconnects.

Then the client is likely being disconnected abnormally on its end of
the connection, probably by a firewall or a faulty network.  The
server cannot detect abnormal disconnects from its end in a timely
fashion.  To do that would require you to implement a keep-alive
mechanism into your data protocol.  This topic has been discussed
numerous times before in several forums, including how to implement
code to handle it.

> However, my application does not detect any form of client
> disconnect, graceful or otherwise (no exceptions are thrown).

It is not supposed to, in this situation.  What you have described is
certainly not a graceful disconnect, which leaves only an abnormal
disconnect.  The OS itself can't detect those right away.  And
unfortunately, the OS has very large timeouts internaly that have to
elapse first before any errors will be reported on abnormaly lost
connections.  So there is nothing Indy can do about it on the server
side.  You have to manage it yourself.

> As far as my application is concerned, the client is still
connected.

As it should be, because the OS itself thinks it is still connected.
Until the OS reports otherwise, Indy will continue thinking so.  That
is why you have to manage keep-alives in your own code.

> Later, the client wants to send some more messages, so it connects
again,
> sends the messages, and then...does nothing again.  Eventually, my
app thinks
> there are 20, 30, maybe 40 connections and the CPU utilization
climbs
> way up to 80-90%.

Your server code needs to keep track of idle periods on each
connection, and then close any connections that sit around doing
nothing for awhile.  If a client wants to keep a connection active for
awhile, it has to send something to the server periodically so that
the connection is not idle for too long.

> Below is a *simplified* snippet of my TCPServerExecute method.

That is hardly simplified.

> Note that the majority of the code here is logging the various
> possible exception cases.

Which is your first mistake.  You are intercepting any possible
exception and not re-throwing any of them for the server to handle
internally.  Indy relies HEAVILY on exceptions.  At the very least,
you should be allowing any exception that is derived from EIdException
to pass back into the server so that it can manage the connections.

> Many of these exception cases are probably overkill

More than that.  You are blocking too much.

> they were added in an effort to try to figure out when/where/if the
> client was disconnecting (partiularly the generic ones), but none of
> the exception cases are ever entered

They won't be until the sockets time out internally.  Which can take
up to several hours, unfortunately.  That is how long Microsoft's
timeouts are set for by default.

You don't need exception handlers to know what is going on with the
client.  ReadFromStack() reports that information.  Try the following
code instead:

    const int MsPerMin = 60000;

    void __fastcall TMyServer::TCPServerExecute(TIdPeerThread
*AThread)
    {
        TMySpecialPeerThread *LThread =
static_cast<TMySpecialPeerThread *>(AThread);

        int i = LThread->Connection->ReadFromStack(false,
(FMaxIdleTime > 0) ? (FMaxIdleTime * MsPerMin) : IdTimeoutInfinite,
false);
        if (i < 0)
        {
            // the client has been inactive for the period given by
FMaxIdleTime (defined in minutes);
            // forcibly disconnect the client and exit
            LogMsg(AnsiString().sprintf( "Disconnecting client due to
inactivity timeout. (%s)", e.Message.c_str() ));
            LThread->Connection->DisconnectSocket();
        }
        else if (i == 0)
        {
            // the client has disconnected normally; just exit
            LogMsg(AnsiString().sprintf( "Client disconnected
gracefully. (%s)", e.Message.c_str() ));
        }
        else
        {
            LogMsg(AnsiString().sprintf( "Received data (%d bytes)
from client.", i);
            LThread->Buffer = LThread->GetBuffer() +
LThread->Connection->InputBuffer->Extract(i);
        }
    }

> Is there a "close packet" (or something similar) that a TCP client
uses
> to indicate to the server that it is disconnecting?

During a graceful disconnect, yes.  Graceful disconnects are
negotiated operations.  There are a few steps involved at the TCP
level to ensure that both parties know the connection is intentionally
being closed right away.

> Is it possible for a client to disconnect without the server knowing
it?

Yes.  It is the client's responsibility to ensure it is closing the
socket properly whenever possible.  Otherwise, the disconnect is
abnormal.

> Any suggestions for how I might go about determining the cause of
> client disconnects not getting detected?

I suggest you turn off any frewalls that are between the client and
server, and use an external packet sniffer to watch the TCP traffic to
ensure everythng is being transmitted properly.


Gambit
Technical Details
Message-ID: <45ac97b6@newsgroups.borland.com>
Path: arc1-ca.pugleaf.net!g2news2.google.com!news3.google.com!news.glorb.com!newsfeeds.ihug.co.nz!ihug.co.nz!newsgroups.borland.com!not-for-mail
References: <45a86ffe$1@newsgroups.borland.com>

Show Full Headers
Complete Article Headers
X-Google-Thread: 11508e,144f6b6a05bf12ad
X-Google-Attributes: gid11508e,public
X-Google-Language: ENGLISH,ASCII-7-bit
From: "Remy Lebeau \(TeamB\)" <no.spam@no.spam.com>
Newsgroups: borland.public.cppbuilder.internet.socket
References: <45a86ffe$1@newsgroups.borland.com>
Subject: Re: Indy TCPServer, client disconnects not detected
Date: Tue, 16 Jan 2007 01:15:59 -0800
Lines: 148
X-Priority: 3
X-MSMail-Priority: Normal
X-Newsreader: Microsoft Outlook Express 6.00.2800.1409
X-MimeOLE: Produced By Microsoft MimeOLE V6.00.2800.1409
NNTP-Posting-Host: 75.83.34.171
Message-ID: <45ac97b6@newsgroups.borland.com>
X-Trace: newsgroups.borland.com 1168938934 75.83.34.171 (16 Jan 2007 01:15:34 -0800)
Path: arc1-ca.pugleaf.net!g2news2.google.com!news3.google.com!news.glorb.com!newsfeeds.ihug.co.nz!ihug.co.nz!newsgroups.borland.com!not-for-mail