Skip to content

Commit

Permalink
Added close_abort() function to WiFiClient
Browse files Browse the repository at this point in the history
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:
   esp8266#230, esp8266#1070, esp8266#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.
  • Loading branch information
pfabri committed Dec 15, 2016
1 parent 7b32e6a commit 9fac290
Show file tree
Hide file tree
Showing 3 changed files with 46 additions and 0 deletions.
9 changes: 9 additions & 0 deletions libraries/ESP8266WiFi/src/WiFiClient.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
1 change: 1 addition & 0 deletions libraries/ESP8266WiFi/src/WiFiClient.h
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,7 @@ class WiFiClient : public Client, public SList<WiFiClient> {
}
virtual void flush();
virtual void stop();
virtual void stop_abort();
virtual uint8_t connected();
virtual operator bool();

Expand Down
36 changes: 36 additions & 0 deletions libraries/ESP8266WiFi/src/include/ClientContext.h
Original file line number Diff line number Diff line change
Expand Up @@ -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()
{
Expand Down Expand Up @@ -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)
{
Expand Down

1 comment on commit 9fac290

@shimizutko
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for your program.
I tried to compile your program as

Line 168 in my WiFiClient.cpp had
size_t WiFiClient::availableForWrite ()
{
return _client->availableForWrite();
}

and also, Line 127 in my ClientContext.h had

size_t availableForWrite ()
{
return _pcb? tcp_sndbuf(_pcb): 0;
}
but, you deleted. I modified as you explained 3 files. The Arduino cpmpiler says Error:
no 'size_t WiFiClient::availableForWrite()' member function declared in class 'WiFiClient'

Do we need some program lines?

Takao

Please sign in to comment.