Ethernet icon indicating copy to clipboard operation
Ethernet copied to clipboard

Automatically reclaim abandoned sockets

Open PaulStoffregen opened this issue 6 years ago • 3 comments

We should probably add a reference count for each socket, to know how many client, server & udp objects are currently referencing it.

http://forum.arduino.cc/index.php?topic=567368.msg3865329#msg3865329

With this info, when socketBegin() doesn't find any sockets available for use, we could detect if any have become unreferenced. Ideally authors of sketches and libraries should properly close or stop every connection. But if they don't do things properly, we can "leak" sockets.

PaulStoffregen avatar Sep 06 '18 22:09 PaulStoffregen

Is it possible to track which socket is the most "stale" by tracking time of last use for each? Then when it's necessary to reclaim sockets, the one not used for the longest could be reclaimed first.

SapientHetero avatar Oct 11 '18 19:10 SapientHetero

I'm running into something like this when I load test my embedded webserver using multiple browsers because the browsers all specify "Connection: keep-alive". My system ends up with 7 open sockets in "ESTABLISHED" state and one in "LISTEN". When I try to open another socket (e.g., to get an NTP update, access a DNS or connect to a remote server) my system can't complete the request until one of the browsers decides to disconnect a socket. These sockets aren't "abandoned", per se; they're just inconveniently hogged by modern browsers for longer than I'd like.

My first thought is to track the time when each socket connected to an Ethernet::Client was last used and disconnect the one that's been idle the longest and has empty Rx and Tx buffers. My server won't have a problem dealing with a previously open socket being poached in order to establish a new connection; the next time a browser connects using that socket, the server will see it as a new client and add it to its current clients list. YMMV.

I researched your reference count idea too. The constructors for client, server and UDP all create objects with sockindex set to MAX_SOCK_NUM. When xxx.begin() is called, an available socket is found and allocated. With that being the case, how would multiple objects end up referencing the same socket? Have I missed something?

I'm very interested in others' thoughts about both reference counts and the idea of forcing the closure of "stale" sockets and leaving the application to check previously-allocated socket statuses before using them. I'll share whatever I develop to solve my problem and you're welcome to use anything you see that you like.

Oh, and I should mention that my testing also produced a problem that left my system with 7 sockets in "TIME_WAIT" state and one in "LISTEN". The solution you left in socket.cpp seems to work when completed as follows:

#if 1								// enabled Jan 30, 2019
	Serial.print("W5000socket step3\n");
	// next, use any that are effectively closed
	for (s=0; s < MAX_SOCK_NUM; s++) {
		uint8_t stat = status[s];
		// TODO: this also needs to check if no more data
		if (stat == SnSR::CLOSE_WAIT && socketRecvAvailable(s) == 0) goto closemakesocket;  // added Jan 30, 2019
	}
#endif

SapientHetero avatar Jan 30 '19 23:01 SapientHetero

I've finished my socket management project and it's been stable in testing for over a month. It may not be perfect as-is for low-RAM boards, but it rocks on my Adafruit Metro M4. Let me know when you're ready to work on this library and I'll post my version of it for you to use as you please. I'll continue testing in the meantime.

SapientHetero avatar Mar 01 '19 20:03 SapientHetero