QNEthernet EthernetClient::close() - How to properly close a connection

dgnuff

Active member
The documentation for QNEthernet::EthernetClient::close() notes that it closes the connection, but does so in a non-blocking fashion. Overall, I really like QNEthernet, because just about everything can be made non-blocking, which is something I find essential for the overall architecture of the project I'm working on.

So what I anticipate doing to close a connection is to call EthenetClient::close(), and then place the EthernetClient class instance in a collection of "clients with close pending". Then in my Tick() method, which is called on a regular basis, I'd want to check if the connection has been fully closed, i.e. FIN->ACK has gone both ways. Only then do I want to delete the EthernetClient, note that during the connect process it was created by a call to new EthernetClient;.

This raises the question of how do I detect that the client is fully disconnected during the Tick() method? Calling EthernetClient::connected() looks promising, but without explicit documentation, I can't be certain that it's not called early in the process, e.g. as soon as the local TCP stack has sent the first FIN packet. If I tear the client down at that point, the remote system will never see an ACK for its FIN, which is not at all desirable.
 
I wrote:

... I can't be certain that it's not called early in the process, e.g. as soon as the local TCP stack has sent the first FIN packet. ...

I can't now eedit the post, but what I should have said was "I can't be certain that it won't start returning false early in the process ..."

That should make a lot more sense, and correctly describe the question I have.
 
The behaviour of those functions is documented here: https://www.arduino.cc/reference/en/libraries/ethernet/
This disambiguation might also help (QNEthernet README): https://github.com/ssilverman/QNEth...ey-of-how-connections-aka-ethernetclient-work

I tried to conform this library to the Arduino Ethernet API, while at the same time adding extra features that you can't do with the limited Arduino API.

If you just want "is this closed" behaviour, try the "operator bool()" form. i.e. Cast the connection object into a bool to test for "closedness". The "connected()" form also tests if there's data available that hasn't yet been read.
 
I'm curious, why are you using `new` to create `EthernetClient`s instead of, say, having a list of them already created? For example:

Code:
std::array<EthernetClient, EthernetClient::maxSockets()> connList;
    // Or some specific number as opposed to the maximum

Or a `std::vector` or whatever...

Note that one of the reasons the implementation is so complex under the covers (`EthernetClient` holds a `ConnectionHolder` which in turn holds a `ConnectionState`) is because the Arduino API allows copy semantics between `EthernetClient` objects. This means than when assigning one to another, they both have to refer to the same underlying "connection".

If you must use dynamically-allocated memory, then consider using smart pointers (i.e. `std::unique_ptr`) instead so that their lifetime is more effectively managed.
 
Last edited:
I'm curious, why are you using `new` to create `EthernetClient`s instead of, say, having a list of them already created? For example:

Code:
std::array<EthernetClient, EthernetClient::maxSockets()> connList;
    // Or some specific number as opposed to the maximum

Or a `std::vector` or whatever...

From the perspective of my application, switching from new/delete to use of a preallocated pool doesn't change much of anything. When a connection is established, I have to get an EthernetClient from somewhere, and assign it to the class instance in my code that's handling this connection. That means either calling new, or asking whatever code is managing the std::vector<EthernetClient> to give me one that's currently not in use. Then when the connection closes, I either call delete, or return the EthernetClient to the pool, making it available for use elsewhere. Nothing really changes.

The "class instance that handles connections" is my own Socket class which acts as a wrapper for EthernetClient, EthernetServer and EthernetUDP, and interfaces them with the rest of the system. The whole thing is built around a small-scale publish/subscribe system, but it takes a fair bit of code (over 1000 LOC) to connect QNEthernet to this. That's a lot of the reason I'm trying to do everything non-blocking. The application architecture is completely based on the idea of modules passing messages through the pub/sub system, when someone gets a message process it as quickly as possible and publish the results to whoever wants them. This has the dual advantages of completely decoupling individual modules from one another, and providing effective multi-tasking since things just naturally timeslice themselves by virtue of the "receive a message, process it, publish the results and return" architecture that everything follows.

This description really doesn't cover it all, but I can't fully describe a 10,000 LOC project in a couple of paragraphs. :) This does however provide an explanation of the basic framework.
 
Did the “operator bool()” vs. “connected()” explanation help?

Yes it did. I'd seen the difference when looking at the code on my original analysis. Given that connected() requires both the connection to be closed and the remaining buffer to be empty, that's the one I'm finding works better for me. operator bool() will return false as soon as the connection closes, even though there may still be data in the remaining buffer, so there's a risk of a race condition where it could return false earlier than I want.

Given the overall polling nature of my socket layer, trying to interact with external events that are completely asynchronous to it i.e. data being received, connected sockets exchanging FIN/ACK sequences etc etc, windows of opportunity for race conditions will exist, and they're typically difficult to track down because reproducing them is so time sensitive.
 
Back
Top