Atozed Forums

Full Version: Closing a TCP/IP component connection the right way
You're currently viewing a stripped down version of our content. View the full version with proper formatting.
Hi,

Let's say that, I have a TCP component with its connection opened with the server, whether it is TIdTCPClient, TIdHTTPTIdFTP, etc. What is the correct way to close the connection without raising any exception especially if the server went down before doing so?

I know that all of these classes inherit from TIdTCPClientCustom so there must be a common way to close them on that class level.

The idea here is that I want to create something similar to std::unique_lock, std::unique_ptr, and std::lock_guard. Let's call it unique_connection<T> for instance. So, regardless of the template T which can be any class that inherits from TIdTCPClientCustom, whenever unique_connection goes out of scope it will close the connection without raising any exceptions whether the server is still running or not.

I need this to make sure that there are no resources like sockets, or connections that are still open before the client gets destroyed.
(03-12-2024, 04:08 PM)Ahmed Sayed Wrote: [ -> ]I know that all of these classes inherit from TIdTCPClientCustom so there must be a common way to close them on that class level.

Simply call their Disconnect() method, which is inherited from TIdTCPConnection. It should not raise an exception, but if it does, just catch it.

In the case of TIdFTP and various other higher-level protocol components, Disconnect() will send a goodbye message to the peer before shutting down the connection. If you don't want to send that message, Disconnect() has an optional ANotifyPeer parameter that you can set to false.

(03-12-2024, 04:08 PM)Ahmed Sayed Wrote: [ -> ]I need this to make sure that there are no resources like sockets, or connections that are still open before the client gets destroyed.

You don't need to worry about that. The client's destructor will shutdown the connection and close the socket if it is still active.
(03-12-2024, 04:44 PM)rlebeau Wrote: [ -> ]
(03-12-2024, 04:08 PM)Ahmed Sayed Wrote: [ -> ]I know that all of these classes inherit from TIdTCPClientCustom so there must be a common way to close them on that class level.

Simply call their Disconnect() method, which is inherited from TIdTCPConnection.  It should not raise an exception, but if it does, just catch it.

In the case of TIdFTP and various other higher-level protocol components, Disconnect() will send a goodbye message to the peer before shutting down the connection.  If you don't want to send that message, Disconnect() has an optional ANotifyPeer parameter that you can set to false.

(03-12-2024, 04:08 PM)Ahmed Sayed Wrote: [ -> ]I need this to make sure that there are no resources like sockets, or connections that are still open before the client gets destroyed.

You don't need to worry about that.  The client's destructor will shutdown the connection and close the socket if it is still active.

The way I know it is that to close the connection from the client side, I have to do something similar to this:

Code:
IdHTTP->IOHandler->WriteBufferClear();
IdHTTP->IOHandler->InputBuffer->Clear();
IdHTTP->IOHandler->Close();
IdHTTP->DisconnectNotifyPeer();
(03-13-2024, 10:28 PM)Ahmed Sayed Wrote: [ -> ]The way I know it is that to close the connection from the client side, I have to do something similar to this:

Code:
IdHTTP->IOHandler->WriteBufferClear();
IdHTTP->IOHandler->InputBuffer->Clear();
IdHTTP->IOHandler->Close();
IdHTTP->DisconnectNotifyPeer();

Most of that stuff is unnecessary:
  • You don't need to call WriteBufferClear() if you don't call WriteBufferOpen() first. Even then, you don't need to call WriteBufferClear() directly if you are using WriteBuffer(Close|Cancel|Flush)() properly to begin with. Also, IOHandler->Open() and IOHandler->Close() both call WriteBufferClear() for you.

  • You don't need to Close() the IOHandler manually, let Disconnect() handle that for you.

  • The only time you should ever need to call InputBuffer->Clear() directly is if you need to reconnect a client after closing a connection prematurely and left unread data in the InputBuffer. The Connect() method raises an exception if Connected() returns True, which it does if there is unread data in the InputBuffer. Under normal usages, if a client follows its protocol properly and you Disconnect() cleanly, there should not be any unread data left behind. Typically, you would Clear() the InputBuffer only after an error had occurred that made you forcibly close the socket.

  • You don't need to Close() the IOHandler manually, let Disconnect() handle that for you.

  • Don't call DisconnectNotifyPeer() directly, call Disconnect() instead with its ANotifyPeer parameter set to true. But in your particular example, that doesn't matter because DisconnectNotifyPeer() doesn't do anything in TIdHTTP, as there is no goodbye message to send to an HTTP server. But even if it did, calling DisconnectNotifyPeer() after Close() is just plain backwards, you can't send anything to the peer after closing the connection.

That being said, HTTP is not a stateful protocol, and TIdHTTP manages the socket connection internally for you, connecting and disconnecting it as needed. You should never have to disconnect TIdHTTP manually.