I'm a little confused about the use of select in your application. Are you using it with blocking sockets?
Thanks for including this information. The ENOMEM gives me a good clue of what’s most likely going on. My guess is that you’re experiencing a memory pool exhaustion and the stale socket has claimed memory from a pool for the segments which are queued for transmit. Since those segments are not being ACKed in the half open state, the claimed memory won’t be available until the segments are freed (happens during transmission timeout or when socket is aborted)
Further, since it sounds like you initially had sockets configured in blocking mode, when the new socket tries to transmit, it will block trying to allocate TCP segments due to the exhausted memory pool. The blocking will continue until SO_SNDTIMEOUT is reached or the memory exhaustion is resolved
If you have LwIP stats enabled, you can check the memory pools for errors to figure out which one is failing. You should be able to resolve this by sizing your memory pools to handle the number of supported connections. For example if you only support 5 simultaneous TCP connections, then your pools should be big enough to allocate 5 send buffers worth of segments. This is how I configure my products, which typically have plenty of RAM. Not sure what the recommendation is for very constrained RAM products.
Calling close() will initiate a graceful synchronized closure of the connection. This means continuing to send any queued data until it is ACKed, the send times out, or we received a RST. Then a FIN is sent indicating the sending pathway is closed.
So there's no direct way for the application to tell LWIP to just give up on one socket without further trying to send data? Can the application specify a send timeout?\
Yes there is, with SO_LINGER you can perform an abortive closure rather than graceful by setting the timeout to 0. Typically this is a bad idea. There’s a decent discussion here on stackoverflow: