Managing Exceptions in Indy TIdTCPServer and TIdTCPClient
I have read in other post of this forum and in other places that in Indy components the exceptions (descendant from EIdException
) are internally managed.
For this reason, in the TCPServer Execute
method there is no need to use a try - catch
block.
But what happens if in the TCPServer Execute
method another kind of exception occurs?
In my server application, I have put a TIdTCPServer
component on the main form, if something bad happens inside its Execute
method and I don't manage it, the server stops.
Obviously I need the server running so I use a try - catch
block and if there is an exception (any kind of exception), I restart the server.
I don't know if this is the best thing to do.
Sometimes, with the server application running in the IDE, I got the error
Project CallMonitor.exe raised exception class EIdSocketError with message 'Socket Error # 10054 - Connection reset by peer.'.
probably caused by the client running on the same computer (not in the IDE). The application execution is blocked and If I break, code is stopped in IdStack file where there is the big READ ME!!! about exception 10038. I press F9 and the application continues running.
For me it is not very clear what happens. My try - catch
block is effective in this situation? Or is it useless or harmful?
I have to filter Indy exceptions and other exceptions?
On the client side, in my application there is a TIdTCPClient object in the main form and the connection to the server is managed in the main thread. Then there is another thread to manage communication with the server. In the communication thread Execute
method I have a try - catch
block and a loop in which I send a request to the server every 2 seconds or when the user asks for data and I decode the server answer.
If there is a EIdReadTimeout
exception, i terminate the thread and I restart it. For other exception, I terminate the thread, I disconnect/connect the TIdTCPClient and I restart the thread.
I think this is a different situation because in the server the exception was managed inside the TIdTCPServer thread while in the client the exception is managed in a thread that just uses the TCPClient->Socket
to communicate with the server, is it right?
Any answer/comment/suggestion is appreciated.
CodePudding user response:
in the TCPServer
Execute
method there is no need to use atry - catch
block. But what happens if in the TCPServerExecute
method another kind of exception occurs?
ANY uncaught exception that is allowed to escape from the OnConnect
or OnExecute
event back into TIdTCPServer
will cause the calling TIdContext
thread to stop running. It will close its associated client Connection
during its cleanup, firing the OnDisconnect
event. And then the OnException
event will be fired afterwards.
In my server application, I have put a
TIdTCPServer
component on the main form, if something bad happens inside itsExecute
method and I don't manage it, the server stops.
The server as a whole does not stop. Only the calling TIdContext
thread is stopped.
Obviously I need the server running so I use a
try - catch
block and if there is an exception (any kind of exception), I restart the server.
It is not necessary to restart the whole server on any exception. Only on exceptions that invalidate/corrupt something that your app needs to function properly.
Sometimes, with the server application running in the IDE, I got the error
Project CallMonitor.exe raised exception class EIdSocketError with message 'Socket Error # 10054 - Connection reset by peer.'.
That is a perfectly normal socket error. That will not kill your whole server.
If you go into your IDE's debugger settings, there is an option to ignore Indy "silent" exceptions (derived from EIdSilentException
, such as EIdConnClosedGracefully
). Or, you can also tell the debugger specific exception types to ignore, such as EIdSocketError
.
The application execution is blocked ... I press F9 and the application continues running.
Exactly. Not a fatal error. The blockage is only in the debugger, it is letting you examine the exception and decide what to do with it.
For me it is not very clear what happens. My
try - catch
block is effective in this situation?
The IDE debugger catches exceptions before your code does. When you press F9 to continue execution, the exception will be passed back to your code, and the appropriate catch
will handle it normally.
Or is it useless or harmful?
No.
I have to filter Indy exceptions and other exceptions?
IF you catch exceptions at all, handle the ones you need, and then you should re-throw any Indy-specific exceptions you caught (all Indy exceptions are derived from EIdException
for easy identification), let the server handle them. If you don't, then you should disconnect the calling Connection
yourself and exit the event handler gracefully. Either way, the server will then cleanup the rest as needed.
On the client side, in my application there is a
TIdTCPClient
object in the main form and the connection to the server is managed in the main thread. Then there is another thread to manage communication with the server.
I would move the connection management into the worker thread as well. Connect, communicate, disconnect, repeat if needed, should all be in one thread.
In the communication thread
Execute
method I have atry - catch
block and a loop in which I send a request to the server every 2 seconds or when the user asks for data and I decode the server answer. If there is aEIdReadTimeout
exception, i terminate the thread and I restart it.
If an actual read operation times out, the state of the communication is unknown and likely unrecoverable, as you don't know which byte was the one that timed out. So you should disconnect and re-connect, not just restart the thread. The only time you wouldn't need a full re-connect is if you are handling the timeout wait yourself in between messages, and know the communication hasn't been corrupted. In which case, simply restarting the thread without a full re-connect is not likely to solve the timeout condition. It would be simpler to just re-try the wait operation again.
For other exception, I terminate the thread, I disconnect/connect the
TIdTCPClient
and I restart the thread.
If the connect/disconnect logic were moved into the thread, they could be done in a loop, then there would be no need to restart the thread itself. Threads are expensive to create/destroy (from the OS's perspective), so try to reuse threads when possible.
I think this is a different situation because in the server the exception was managed inside the
TIdTCPServer
thread while in the client the exception is managed in a thread that just uses theTCPClient->Socket
to communicate with the server, is it right?
Yes.