From 9fac2903ebad14752bfaa2324f01e526240bc2f3 Mon Sep 17 00:00:00 2001 From: Peter Fabri Date: Thu, 15 Dec 2016 17:31:08 +0100 Subject: [PATCH] Added close_abort() function to WiFiClient This branch adds a close_abort() method to the WiFiClient class. How it works, what it does: Calling `close_abort()` will close and abort the client connection it is invoked on and, as a result, free its resources (i.e. memory). **WARNING:** aborting connections without a good reason violates the TCP protocol, because a closed connection would normally need to spend some time in `TIME_WAIT` state before its resources are freed. Usage example: WiFiClient client; // set up a client { /* do things with your client */ } client.stop_abort() // when you're done,abort the // connection if you must Why it's useful: 1. Give programmers a way to shut down connections immediately if need be. The underlying `tcp.c` file has an abort function, but this has not been directly accessible via the `WiFiClient` class until now. 2. There are a number of reported issues for the repository addressing the heap corruption that can result from trying to retain too many connections in `TIME_WAIT` state (most notably: #230, #1070, #1923). Although the warning above holds, there may be circumstances where this isn't very important. For example an ESP8266 running in AP mode hosting a page, which requests a new connection every second via an AJAX script to monitor a sensor/button/etc. continusously. Currently existing alternative approach: When building a project, defining the `-D MEMP_NUM_TCP_PCB_TIME_WAIT=5`compiler directive will limit the maximum number of clients allowed to stay in TIME_WAIT state. `5` is the default, lower it as necessary. See reference [here](https://github.com/esp8266/Arduino/blob/master/tools/sdk/lwip/include/lwipopts.h#L263) Thanks: Thank you to @me-no-dev, @everslick and @Palatis for bringing the ` MEMP_NUM_TCP_PCB_TIME_WAIT` option to my attention. --- libraries/ESP8266WiFi/src/WiFiClient.cpp | 9 +++++ libraries/ESP8266WiFi/src/WiFiClient.h | 1 + .../ESP8266WiFi/src/include/ClientContext.h | 36 +++++++++++++++++++ 3 files changed, 46 insertions(+) diff --git a/libraries/ESP8266WiFi/src/WiFiClient.cpp b/libraries/ESP8266WiFi/src/WiFiClient.cpp index 75a550b6ed..53d6c7e934 100644 --- a/libraries/ESP8266WiFi/src/WiFiClient.cpp +++ b/libraries/ESP8266WiFi/src/WiFiClient.cpp @@ -269,6 +269,15 @@ void WiFiClient::stop() _client = 0; } +void WiFiClient::stop_abort() //Compare this to ::stop +{ + if (!_client) + return; + + _client->unref_abort(); + _client = 0; +} + uint8_t WiFiClient::connected() { if (!_client) diff --git a/libraries/ESP8266WiFi/src/WiFiClient.h b/libraries/ESP8266WiFi/src/WiFiClient.h index dabd65f26e..ce5b072ed0 100644 --- a/libraries/ESP8266WiFi/src/WiFiClient.h +++ b/libraries/ESP8266WiFi/src/WiFiClient.h @@ -64,6 +64,7 @@ class WiFiClient : public Client, public SList { } virtual void flush(); virtual void stop(); + virtual void stop_abort(); virtual uint8_t connected(); virtual operator bool(); diff --git a/libraries/ESP8266WiFi/src/include/ClientContext.h b/libraries/ESP8266WiFi/src/include/ClientContext.h index d2ee0903b3..d4f2870c1b 100644 --- a/libraries/ESP8266WiFi/src/include/ClientContext.h +++ b/libraries/ESP8266WiFi/src/include/ClientContext.h @@ -86,6 +86,27 @@ class ClientContext } return err; } + + err_t close_abort() + { + /* ERR_ABRT must be sent back on abortion as specified in the comments + * of tools/sdk/lwip/src/core/tcp.c at the footnote of tcp_abort() */ + err_t err = ERR_ABRT; + if(_pcb) { + DEBUGV(":close\r\n"); + tcp_arg(_pcb, NULL); + tcp_sent(_pcb, NULL); + tcp_recv(_pcb, NULL); + tcp_err(_pcb, NULL); + tcp_close(_pcb); + /* Without delay some clients fail to receive the response and + * report a 'cannot connect' error message */ + delay(10); + tcp_abort(_pcb); + _pcb = 0; + } + return err; + } ~ClientContext() { @@ -123,6 +144,21 @@ class ClientContext } } } + + void unref_abort() + { + if(this != 0) { + DEBUGV(":ur_abrt %d\r\n", _refcnt); + if(--_refcnt == 0) { + flush(); + close_abort(); + if(_discard_cb) + _discard_cb(_discard_cb_arg, this); + DEBUGV(":del\r\n"); + delete this; + } + } + } void setNoDelay(bool nodelay) {