🚀 go-pugleaf

RetroBBS NetNews Server

Inspired by RockSolid Light RIP Retro Guy

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

Re: TClientSocket handling a bad connection

#384
From: Lesley Anne
Date: Wed, 02 Apr 2008 00:12
214 lines
10785 bytes
"Remy Lebeau (TeamB)" wrote in message 
news:47f27f91$1@newsgroups.borland.com...
>
> "Lesley Anne" wrote in message news:47f26db7@newsgroups.borland.com...
>
> If you are getting the OnConnect event fired, then the FSocket member 
> should be valid, and Disconnect() will fire the OnDisconnect event the 
> first time it is called, even after a send error occurs.

Disconnect should fire the OnDisconnect event, but somehow it isn't 
happening, at least not once this error occurs.

Here are my event handlers.  I should see every time the OnConnect or 
OnDisconnect events fire, and the first time the OnError event fires after 
an OnConnect or OnDisconnect.  (I do suppress display of subsequent error 
messages in order to avoid having the app try to write hundreds of identical 
error messages every second when a problem is occurring.  Because of this, I 
only know that the first error I get is WSAENOTSOCK.  There may be others 
after this that I'm not seeing.  I might change this to also display any 
error if its ErrorCode is different than the last one received.)

// ----------------------------------------------------------------------
void __fastcall TForm1::BEClientSocketConnect(TObject *Sender,
                                              TCustomWinSocket *Socket)
{
   LogMessage("Connection Established to Back End. " + 
BEClientSocket->Address);
   BEIndicator->Brush->Color = clLime;
   BEError1Displayed = false;
   BEError2Displayed = false;
   UpdateAlarms();
}
// ----------------------------------------------------------------------
void __fastcall TForm1::BEClientSocketDisconnect(TObject *Sender,
                                                 TCustomWinSocket *Socket)
{
   LogMessage("Connection Dropped to Back End. " + BEClientSocket->Address);
   BEIndicator->Brush->Color = clRed;
   BEError1Displayed = false;
   BEError2Displayed = false;
   UpdateAlarms();
}
// ----------------------------------------------------------------------
void __fastcall TForm1::BEClientSocketError(TObject *Sender,
        TCustomWinSocket *Socket, TErrorEvent ErrorEvent, int &ErrorCode)
{
   if (!BEError1Displayed) {
      String ErrorMessage = "ERROR: Back End Socket Error, ErrorEvent=";
      switch (ErrorEvent) {
         case eeGeneral:      ErrorMessage += "General";        break;
         case eeSend:         ErrorMessage += "Send";           break;
         case eeReceive:      ErrorMessage += "Receive";        break;
         case eeConnect:      ErrorMessage += "Connect";        break;
         case eeDisconnect:   ErrorMessage += "Disconnect";     break;
         case eeAccept:       ErrorMessage += "Accept";         break;
         default:             ErrorMessage += "(unrecognized)";
      }
      ErrorMessage += " ErrorCode=" + String(ErrorCode);
      LogMessage(ErrorMessage);

      BEError1Displayed = true;
   }

   BEIndicator->Brush->Color = clRed;
   ErrorCode = 0;
   Socket->Close();
}
// ----------------------------------------------------------------------

The event handlers all do fire until this error occurs.  The error event 
calls Socket->Close(), which should then give an OnDisconnect, but that 
never happens.  Neither does the Active property get set to false, as should 
also happen in the call to Event(Self, seDisconnect).  Somehow Close appears 
not to be making it all the way through the Disconnect function.  If it's 
not due to the exit after that INVALID_SOCKET check, then the only other 
things I can see that could be happening is that either the try block in 
Disconnect somehow isn't making it past the first few lines, and is jumping 
out to its finally block before reaching the Event call, or else it's 
picking up TCustomWinSocket.Event instead of TCustomSocket.Event which is 
where OnDisconnect is.  (Actually, I don't really see how it would pick up 
the TCustomSocket version of Event, but if it wasn't, then the events 
wouldn't have been working in the first place.)


{ ***************************************** }
{     from  ScktComp.pas                    }
{ ***************************************** }

procedure TCustomWinSocket.Close;
begin
  Disconnect(FSocket);
end;

procedure TCustomWinSocket.Disconnect(Socket: TSocket);
begin
  Lock;
  try
    if FLookupHandle <> 0 then
      CheckSocketResult(WSACancelASyncRequest(FLookupHandle), 
'WSACancelASyncRequest');
    FLookupHandle := 0;
    if (Socket = INVALID_SOCKET) or (Socket <> FSocket) then exit;
    Event(Self, seDisconnect);
    CheckSocketResult(closesocket(FSocket), 'closesocket');
    FSocket := INVALID_SOCKET;
    FAddr.sin_family := PF_INET;
    FAddr.sin_addr.s_addr := INADDR_ANY;
    FAddr.sin_port := 0;
    FConnected := False;
    FreeAndNil(FSendStream);
  finally
    Unlock;
  end;
end;

procedure TCustomWinSocket.Event(Socket: TCustomWinSocket; SocketEvent: 
TSocketEvent);
begin
  if Assigned(FOnSocketEvent) then FOnSocketEvent(Self, Socket, 
SocketEvent);
end;

procedure TCustomSocket.Event(Socket: TCustomWinSocket; SocketEvent: 
TSocketEvent);
begin
  case SocketEvent of
    { snip other cases }
    seDisconnect:
      begin
        FActive := False;
        if Assigned(FOnDisconnect) then FOnDisconnect(Self, Socket);
      end;
  end;
end;


{ ***************************************** }


Also, in addition to that call to Socket->Close in the OnError handler 
itself, I also have the ReconnectTimer (shown at the end of my first post in 
this thread) which is periodically attempting
         BEClientSocket->Close();
         BEClientSocket->Open();
for as long as the socket remains in this error state (as shown by that 
indicator being set to clRed).  None of these Close or Open attempts work 
either.  Once the socket gets this error, neither OnConnect nor OnDisconnect 
events occur again.  Active remains true, but there is no communication with 
the server app (which times out after a while, closing its side of the 
connection).

Well, tonight a couple of my co-workers are going to set up a trap in the 
nearest router to see what it sees going across the connection.  Maybe that 
will shed some light on what's happening.  The only other thing I know right 
now is that the problem occurs around midnight when the server closes the 
connection for a few moments to do some other processing.  When it re-opens 
and the clients reconnect (or try to) that's when the remote client gets 
this error.


> That is the pitfall to using non-blocking sockets in general.  Every 
> operation you perform may put the socket into a busy state.  If you are 
> sending data when it happens, you must wait for the socket to report it is 
> ready again before you can send data again.  That is what the OnWrite 
> event is for - the socket reporting that it has become writable.  Most 
> people who use non-blocking sockets ignore that step, mainly because they 
> simply don't know any better.  It it not common to hit on, but it still 
> possible and should be coded for.

I seem to be hitting on it.  Even apart from this weird error stage, even my 
good connections are occasionally dropping some data where the server 
doesn't see as much as the client thinks it's sending, (particularly when 
traffic is heavy).  It sounds like missing this OnWrite processing is the 
culprit for that problem.


> When I use non-blocking sockets (which is not common nowadays, I usually
> use blocking instead), I use an intermediate buffer when needed.  When 
> writing any data to a connection, I first check if the buffer is empty. 
> If it is not, I add the data into the buffer and move on.  Otherwise, I 
> pass the data to the socket instead, and if it reports -1 without an 
> OnError event (TCustomWinSocket does not fire the OnError event for 
> WSAEWOULDBLOCK) then I put the data into the buffer and move on.  Either 
> way, whenever the OnWrite event is fired, I pass whatever data is in the 
> buffer to the socket, removing only as much as the socket said it could 
> accept (if you have a lot of data buffered, another WSAEWOULDBLOCK could 
> occur while you are still inside the OnWrite event).
>
> Here is some code to demonstrate it better:

Thanks.  I think I see how this is supposed to work now.  I'll work with 
that a little and see if I can adapt it to my app.  (I'm also trying to keep 
track of how many messages have been sent, so I can compare it to what the 
server is receiving at its end.  And any messages that haven't been sent 
during one connection need to be saved and sent next time I reconnect.  I 
should be able to adapt that Buffer for those concerns, though.)  Or I may 
still end up deciding to switch to a blocking socket anyway, although that 
brings in its own complexities with threading.  (Incidentally, which is the 
right newsgroup on which to ask questions about threads and critical 
sections?)


>     // Otherwise, an exception would have been raised, unless the
>     // OnError handler set the ErrorCode to 0, which I do not
>     // recomment in this situation...

Currently I do have the event handler setting ErrorCode to 0.  This is 
because, since the socket is non-blocking and continues to operate after 
having returned from the function call, I would no longer be in the 
try-catch block at that point.  I'm worried about errors that occur on the 
socket that occur after having returned from the SendText or SendBuf call 
with WSAEWOULDBLOCK.  The socket itself is still sending, so presumably 
could still hit a sending error.  I want to make sure that my OnError 
handler finishes handling any error that I don't have a catch for.
Technical Details
Message-ID: <47f2cfe3@newsgroups.borland.com>
Path: arc1-ca.pugleaf.net!g2news1.google.com!news3.google.com!news.germany.com!news.isoc.lu!newsgroups.borland.com!not-for-mail
References: <47f16766$1@newsgroups.borland.com> <47f16f19$1@newsgroups.borland.com> <47f26db7@newsgroups.borland.com> <47f27f91$1@newsgroups.borland.com>

Show Full Headers
Complete Article Headers
X-Google-Thread: 11508e,74b475fd14f26423
X-Google-Attributes: gid11508e,domainid0,public,usenet
X-Google-Language: ENGLISH,ASCII-7-bit
From: "Lesley Anne" <mspfila (dash) brl (at) yahoo (dot) com>
Newsgroups: borland.public.cppbuilder.internet.socket
References: <47f16766$1@newsgroups.borland.com> <47f16f19$1@newsgroups.borland.com> <47f26db7@newsgroups.borland.com> <47f27f91$1@newsgroups.borland.com>
Subject: Re: TClientSocket handling a bad connection
Date: Tue, 1 Apr 2008 20:12:13 -0400
Lines: 216
X-Priority: 3
X-MSMail-Priority: Normal
X-Newsreader: Microsoft Outlook Express 6.00.2900.3138
X-RFC2646: Format=Flowed; Response
X-MimeOLE: Produced By Microsoft MimeOLE V6.00.2900.3198
NNTP-Posting-Host: 144.160.5.25
Message-ID: <47f2cfe3@newsgroups.borland.com>
X-Trace: newsgroups.borland.com 1207095267 144.160.5.25 (1 Apr 2008 17:14:27 -0700)
Path: arc1-ca.pugleaf.net!g2news1.google.com!news3.google.com!news.germany.com!news.isoc.lu!newsgroups.borland.com!not-for-mail