🚀 go-pugleaf

RetroBBS NetNews Server

Inspired by RockSolid Light RIP Retro Guy

Thread View: borland.public.cppbuilder.internet.socket
3 messages
3 total messages Started by Dennis Jones Sat, 13 Jan 2007 05:37
Indy TCPServer, client disconnects not detected
#1520
Author: Dennis Jones
Date: Sat, 13 Jan 2007 05:37
142 lines
6091 bytes
Hello,

I have a server application that uses the Indy TCPServer component.  I allow
any number of connections, but typically expect to see only one or two.  My
application has been working fine for about a year at numerous sites, but 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.

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.  However, my
application does not detect any form of client disconnect, graceful or
otherwise (no exceptions are thrown).  As far as my application is
concerned, the client is still connected.  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%.

Below is a *simplified* snippet of my TCPServerExecute method.  Note that
the majority of the code here is logging the various possible exception
cases.  Many of these exception cases are probably overkill, but 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:

void __fastcall TMyServer::TCPServerExecute(TIdPeerThread *AThread)
{
   boost::scoped_ptr<TStringStream> AutoStream( new TStringStream("") );
   TStringStream *FStream = AutoStream.get();
   TMySpecialPeerThread *LThread = dynamic_cast<TMySpecialPeerThread
*>(AThread);
   try
   {
      static const int MsPerMin = 60000;

      // wait for data
      LThread->Connection->ReadFromStack( true,
                                          (FMaxIdleTime > 0)
                                             ? (FMaxIdleTime * MsPerMin)
                                             : IdTimeoutInfinite,
                                          true);
   }
   catch( EIdConnClosedGracefully &e )
   {
      // if EIdConnClosedGracefully occurs, the client has disconnected
      // normally; just exit
      LogMsg(AnsiString().sprintf( "Client disconnected gracefully. (%s)",
                                   e.Message.c_str() ));
      return;
   }
   catch( EIdReadTimeout &e )
   {
      try
      {
         // if EIdReadTimeout occurs, 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();
      }
      catch( const Exception &e )
      {
         LogMsg(AnsiString().sprintf( "An exception occured while
disconnecting client. (%s)",
                                      e.Message.c_str() ));
      }
      catch(...)
      {
         LogMsg("An unknown exception occured while disconnecting client.");
      }
      return;
   }
   catch( const Exception &e )
   {
      try
      {
         LogMsg(AnsiString().sprintf( "Disconnecting client due to a
connection exception. (%s)",
                                      e.Message.c_str() ));
         LThread->Connection->DisconnectSocket();
      }
      catch( const Exception &e )
      {
         LogMsg(AnsiString().sprintf( "An exception occured while
disconnecting client. (%s)",
                                      e.Message.c_str() ));
      }
      catch(...)
      {
         LogMsg("An unknown exception occured while disconnecting client.");
      }
      return;
   }
   catch(...)
   {
      try
      {
         LogMsg("Disconnecting client due to an unknown connection
exception.");
         LThread->Connection->DisconnectSocket();
      }
      catch( const Exception &e )
      {
         LogMsg(AnsiString().sprintf( "An exception occured while
disconnecting client. (%s)",
                                      e.Message.c_str() ));
      }
      catch(...)
      {
         LogMsg("An unknown exception occured while disconnecting client.");
      }
      return;
   }
   int FSize = (int)LThread->Connection->InputBuffer->Size;
   if ( FSize > 0 )
   {
      LogMsg(AnsiString().sprintf( "Received data (%d bytes) from client.",
FSize);

      FStream->Size = 0;
      LThread->Connection->ReadStream(FStream, FSize);
      LThread->Buffer = LThread->GetBuffer() + FStream->DataString;
   }
}

To my knowledge, this has never occured before, but maybe there is something
unusual about this particular client application.  Is there a "close packet"
(or something similar) that a TCP client uses to indicate to the server that
it is disconnecting?  Is it possible for a client to disconnect without the
server knowing it?

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

Thanks in advance,

- Dennis
Re: Indy TCPServer, client disconnects not detected
#1430
Author: 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
Re: Indy TCPServer, client disconnects not detected
#1434
Author: Dennis Jones
Date: Tue, 16 Jan 2007 16:22
79 lines
4018 bytes
"Remy Lebeau (TeamB)" <no.spam@no.spam.com> wrote in message 
news:45ac97b6@newsgroups.borland.com...
>
> 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.

I know that!  I just don't know what is wrong yet because the application 
has been working fine until this abnormal disconnect issue came up!


> 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.

It isn't *my* protocol and keep-alive isn't supported.


> What you have described is
> certainly not a graceful disconnect, which leaves only an abnormal
> disconnect.

Okay, that's good to know, and gives me something to pass on to the 
customer.


> 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.

Which I am already doing...


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

Sorry, I meant logic-wise.  All the code does is receive the data and handle 
exceptions.


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

Okay <code snipped>, I'm not an Indy expert and wasn't aware of this method. 
Thanks for the code, though I probably won't change anything right away 
since it doesn't solve the immediate problem (but I will pull out the debug 
exception handlers).


> During a graceful disconnect, yes.  Graceful disconnects are
> negotiated operations.

Didn't know that either.  Thank you.


>> 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.

There probably are not any firewalls between the client and the server, but 
it is possible -- I'll have to pass that question along to the customer.  I 
have no access to any of my customers' systems as they have very tight 
security and legal restrictions they must adhere to.  Therefore, running a 
packet sniffer is definitely out of the question, not to mention that they 
are probably on the other side of the country, which makes it impossible for 
me to do anything directly.  So I'm going to have to resolve this on my end.

Thanks for the feedback and the ReadFromStack tip.  I'm sure it will come in 
handy.

- Dennis 
Thread Navigation

This is a paginated view of messages in the thread with full content displayed inline.

Messages are displayed in chronological order, with the original post highlighted in green.

Use pagination controls to navigate through all messages in large threads.

Back to All Threads