Article View: borland.public.cppbuilder.internet.socket
Article #384Re: TClientSocket handling a bad connection
From: Lesley Anne
Date: Wed, 02 Apr 2008 00:12
Date: Wed, 02 Apr 2008 00:12
214 lines
10785 bytes
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-mailReferences:
<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