mirror of
https://github.com/esp8266/Arduino.git
synced 2025-06-06 05:21:22 +03:00
ClientContext (tcp) updates (#5089)
* +sync, get/set default nodelay, sync * default nodelay=1 * update flush() * fix return value * ClientContext: put things together * ClientContext: fix debugging messages * WiFiClient: move static members out of the class, add comments * remove circular dependency * parameter and return value for Client::flush&stop, flush timeout raised to 300ms * tcp flush: restart timer on ack receive * OTA protocol needs setNoDelay(true) * fix Ethernet with Client changes * 1 line unredable -> 5 lines readable code * doc * Update client-class.rst * Added details for getters
This commit is contained in:
parent
88bd26bd74
commit
83a8076db8
@ -34,8 +34,8 @@ class Client: public Stream {
|
|||||||
virtual int read() = 0;
|
virtual int read() = 0;
|
||||||
virtual int read(uint8_t *buf, size_t size) = 0;
|
virtual int read(uint8_t *buf, size_t size) = 0;
|
||||||
virtual int peek() = 0;
|
virtual int peek() = 0;
|
||||||
virtual void flush() = 0;
|
virtual bool flush(unsigned int maxWaitMs = 0) = 0;
|
||||||
virtual void stop() = 0;
|
virtual bool stop(unsigned int maxWaitMs = 0) = 0;
|
||||||
virtual uint8_t connected() = 0;
|
virtual uint8_t connected() = 0;
|
||||||
virtual operator bool() = 0;
|
virtual operator bool() = 0;
|
||||||
protected:
|
protected:
|
||||||
|
@ -18,6 +18,17 @@ Methods documented for `Client <https://www.arduino.cc/en/Reference/WiFiClientCo
|
|||||||
|
|
||||||
Methods and properties described further down are specific to ESP8266. They are not covered in `Arduino WiFi library <https://www.arduino.cc/en/Reference/WiFi>`__ documentation. Before they are fully documented please refer to information below.
|
Methods and properties described further down are specific to ESP8266. They are not covered in `Arduino WiFi library <https://www.arduino.cc/en/Reference/WiFi>`__ documentation. Before they are fully documented please refer to information below.
|
||||||
|
|
||||||
|
flush and stop
|
||||||
|
~~~~~~~~~~~~~~
|
||||||
|
|
||||||
|
``flush(timeoutMs)`` and ``stop(timeoutMs)`` both have now an optional argument: ``timeout`` in millisecond, and both return a boolean.
|
||||||
|
|
||||||
|
Default input value 0 means that effective value is left at the discretion of the implementer.
|
||||||
|
|
||||||
|
``flush()`` returning ``true`` indicates that output data have effectively been sent, and ``false`` that a timeout has occurred.
|
||||||
|
|
||||||
|
``stop()`` returns ``false`` in case of an issue when closing the client (for instance a timed-out ``flush``). Depending on implementation, its parameter can be passed to ``flush()``.
|
||||||
|
|
||||||
setNoDelay
|
setNoDelay
|
||||||
~~~~~~~~~~
|
~~~~~~~~~~
|
||||||
|
|
||||||
@ -35,6 +46,47 @@ This algorithm is intended to reduce TCP/IP traffic of small packets sent over t
|
|||||||
|
|
||||||
client.setNoDelay(true);
|
client.setNoDelay(true);
|
||||||
|
|
||||||
|
getNoDelay
|
||||||
|
~~~~~~~~~~
|
||||||
|
|
||||||
|
Returns whether NoDelay is enabled or not for the current connection.
|
||||||
|
|
||||||
|
setSync
|
||||||
|
~~~~~~~
|
||||||
|
|
||||||
|
This is an experimental API that will set the client in synchronized mode.
|
||||||
|
In this mode, every ``write()`` is flushed. It means that after a call to
|
||||||
|
``write()``, data are ensured to be received where they went sent to (that is
|
||||||
|
``flush`` semantic).
|
||||||
|
|
||||||
|
When set to ``true`` in ``WiFiClient`` implementation,
|
||||||
|
|
||||||
|
- It slows down transfers, and implicitely disable the Nagle algorithm.
|
||||||
|
|
||||||
|
- It also allows to avoid a temporary copy of data that otherwise consumes
|
||||||
|
at most ``TCP_SND_BUF`` = (2 * ``MSS``) bytes per connection,
|
||||||
|
|
||||||
|
getSync
|
||||||
|
~~~~~~~
|
||||||
|
|
||||||
|
Returns whether Sync is enabled or not for the current connection.
|
||||||
|
|
||||||
|
setDefaultNoDelay and setDefaultSync
|
||||||
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
|
These set the default value for both ``setSync`` and ``setNoDelay`` for
|
||||||
|
every future instance of ``WiFiClient`` (including those coming from
|
||||||
|
``WiFiServer.available()`` by default).
|
||||||
|
|
||||||
|
Default values are false for both ``NoDelay`` and ``Sync``.
|
||||||
|
|
||||||
|
This means that Nagle is enabled by default *for all new connections*.
|
||||||
|
|
||||||
|
getDefaultNoDelay and getDefaultSync
|
||||||
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
|
Return the values to be used as default for NoDelay and Sync for all future connections.
|
||||||
|
|
||||||
Other Function Calls
|
Other Function Calls
|
||||||
~~~~~~~~~~~~~~~~~~~~
|
~~~~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
@ -54,7 +106,6 @@ Other Function Calls
|
|||||||
uint16_t remotePort ()
|
uint16_t remotePort ()
|
||||||
IPAddress localIP ()
|
IPAddress localIP ()
|
||||||
uint16_t localPort ()
|
uint16_t localPort ()
|
||||||
bool getNoDelay ()
|
|
||||||
|
|
||||||
Documentation for the above functions is not yet prepared.
|
Documentation for the above functions is not yet prepared.
|
||||||
|
|
||||||
|
@ -32,6 +32,10 @@ This algorithm is intended to reduce TCP/IP traffic of small packets sent over t
|
|||||||
server.begin();
|
server.begin();
|
||||||
server.setNoDelay(true);
|
server.setNoDelay(true);
|
||||||
|
|
||||||
|
By default, ``nodelay`` value will depends on global ``WiFiClient::getDefaultNoDelay()`` (currently false by default).
|
||||||
|
|
||||||
|
However, a call to ``wiFiServer.setNoDelay()`` will override ``NoDelay`` for all new ``WiFiClient`` provided by the calling instance (``wiFiServer``).
|
||||||
|
|
||||||
Other Function Calls
|
Other Function Calls
|
||||||
~~~~~~~~~~~~~~~~~~~~
|
~~~~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
|
@ -290,6 +290,8 @@ void ArduinoOTAClass::_runUpdate() {
|
|||||||
}
|
}
|
||||||
_state = OTA_IDLE;
|
_state = OTA_IDLE;
|
||||||
}
|
}
|
||||||
|
// OTA sends little packets
|
||||||
|
client.setNoDelay(true);
|
||||||
|
|
||||||
uint32_t written, total = 0;
|
uint32_t written, total = 0;
|
||||||
while (!Update.isFinished() && client.connected()) {
|
while (!Update.isFinished() && client.connected()) {
|
||||||
|
@ -43,6 +43,34 @@ extern "C"
|
|||||||
|
|
||||||
uint16_t WiFiClient::_localPort = 0;
|
uint16_t WiFiClient::_localPort = 0;
|
||||||
|
|
||||||
|
static bool defaultNoDelay = false; // false == Nagle enabled by default
|
||||||
|
static bool defaultSync = false;
|
||||||
|
|
||||||
|
bool getDefaultPrivateGlobalSyncValue ()
|
||||||
|
{
|
||||||
|
return defaultSync;
|
||||||
|
}
|
||||||
|
|
||||||
|
void WiFiClient::setDefaultNoDelay (bool noDelay)
|
||||||
|
{
|
||||||
|
defaultNoDelay = noDelay;
|
||||||
|
}
|
||||||
|
|
||||||
|
void WiFiClient::setDefaultSync (bool sync)
|
||||||
|
{
|
||||||
|
defaultSync = sync;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool WiFiClient::getDefaultNoDelay ()
|
||||||
|
{
|
||||||
|
return defaultNoDelay;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool WiFiClient::getDefaultSync ()
|
||||||
|
{
|
||||||
|
return defaultSync;
|
||||||
|
}
|
||||||
|
|
||||||
template<>
|
template<>
|
||||||
WiFiClient* SList<WiFiClient>::_s_first = 0;
|
WiFiClient* SList<WiFiClient>::_s_first = 0;
|
||||||
|
|
||||||
@ -60,6 +88,9 @@ WiFiClient::WiFiClient(ClientContext* client)
|
|||||||
_timeout = 5000;
|
_timeout = 5000;
|
||||||
_client->ref();
|
_client->ref();
|
||||||
WiFiClient::_add(this);
|
WiFiClient::_add(this);
|
||||||
|
|
||||||
|
setSync(defaultSync);
|
||||||
|
setNoDelay(defaultNoDelay);
|
||||||
}
|
}
|
||||||
|
|
||||||
WiFiClient::~WiFiClient()
|
WiFiClient::~WiFiClient()
|
||||||
@ -91,7 +122,6 @@ WiFiClient& WiFiClient::operator=(const WiFiClient& other)
|
|||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
int WiFiClient::connect(const char* host, uint16_t port)
|
int WiFiClient::connect(const char* host, uint16_t port)
|
||||||
{
|
{
|
||||||
IPAddress remote_addr;
|
IPAddress remote_addr;
|
||||||
@ -147,6 +177,9 @@ int WiFiClient::connect(IPAddress ip, uint16_t port)
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
setSync(defaultSync);
|
||||||
|
setNoDelay(defaultNoDelay);
|
||||||
|
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -156,12 +189,26 @@ void WiFiClient::setNoDelay(bool nodelay) {
|
|||||||
_client->setNoDelay(nodelay);
|
_client->setNoDelay(nodelay);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool WiFiClient::getNoDelay() {
|
bool WiFiClient::getNoDelay() const {
|
||||||
if (!_client)
|
if (!_client)
|
||||||
return false;
|
return false;
|
||||||
return _client->getNoDelay();
|
return _client->getNoDelay();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void WiFiClient::setSync(bool sync)
|
||||||
|
{
|
||||||
|
if (!_client)
|
||||||
|
return;
|
||||||
|
_client->setSync(sync);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool WiFiClient::getSync() const
|
||||||
|
{
|
||||||
|
if (!_client)
|
||||||
|
return false;
|
||||||
|
return _client->getSync();
|
||||||
|
}
|
||||||
|
|
||||||
size_t WiFiClient::availableForWrite ()
|
size_t WiFiClient::availableForWrite ()
|
||||||
{
|
{
|
||||||
return _client? _client->availableForWrite(): 0;
|
return _client? _client->availableForWrite(): 0;
|
||||||
@ -264,19 +311,25 @@ size_t WiFiClient::peekBytes(uint8_t *buffer, size_t length) {
|
|||||||
return _client->peekBytes((char *)buffer, count);
|
return _client->peekBytes((char *)buffer, count);
|
||||||
}
|
}
|
||||||
|
|
||||||
void WiFiClient::flush()
|
bool WiFiClient::flush(unsigned int maxWaitMs)
|
||||||
{
|
|
||||||
if (_client)
|
|
||||||
_client->wait_until_sent();
|
|
||||||
}
|
|
||||||
|
|
||||||
void WiFiClient::stop()
|
|
||||||
{
|
{
|
||||||
if (!_client)
|
if (!_client)
|
||||||
return;
|
return true;
|
||||||
|
|
||||||
flush();
|
if (maxWaitMs == 0)
|
||||||
_client->close();
|
maxWaitMs = WIFICLIENT_MAX_FLUSH_WAIT_MS;
|
||||||
|
return _client->wait_until_sent(maxWaitMs);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool WiFiClient::stop(unsigned int maxWaitMs)
|
||||||
|
{
|
||||||
|
if (!_client)
|
||||||
|
return true;
|
||||||
|
|
||||||
|
bool ret = flush(maxWaitMs); // virtual, may be ssl's
|
||||||
|
if (_client->close() != ERR_OK)
|
||||||
|
ret = false;
|
||||||
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
uint8_t WiFiClient::connected()
|
uint8_t WiFiClient::connected()
|
||||||
|
@ -28,7 +28,12 @@
|
|||||||
#include "IPAddress.h"
|
#include "IPAddress.h"
|
||||||
#include "include/slist.h"
|
#include "include/slist.h"
|
||||||
|
|
||||||
#define WIFICLIENT_MAX_PACKET_SIZE 1460
|
#ifndef TCP_MSS
|
||||||
|
#define TCP_MSS 1460 // lwip1.4
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#define WIFICLIENT_MAX_PACKET_SIZE TCP_MSS
|
||||||
|
#define WIFICLIENT_MAX_FLUSH_WAIT_MS 300
|
||||||
|
|
||||||
#define TCP_DEFAULT_KEEPALIVE_IDLE_SEC 7200 // 2 hours
|
#define TCP_DEFAULT_KEEPALIVE_IDLE_SEC 7200 // 2 hours
|
||||||
#define TCP_DEFAULT_KEEPALIVE_INTERVAL_SEC 75 // 75 sec
|
#define TCP_DEFAULT_KEEPALIVE_INTERVAL_SEC 75 // 75 sec
|
||||||
@ -67,8 +72,8 @@ public:
|
|||||||
size_t peekBytes(char *buffer, size_t length) {
|
size_t peekBytes(char *buffer, size_t length) {
|
||||||
return peekBytes((uint8_t *) buffer, length);
|
return peekBytes((uint8_t *) buffer, length);
|
||||||
}
|
}
|
||||||
virtual void flush();
|
virtual bool flush(unsigned int maxWaitMs = 0);
|
||||||
virtual void stop();
|
virtual bool stop(unsigned int maxWaitMs = 0);
|
||||||
virtual uint8_t connected();
|
virtual uint8_t connected();
|
||||||
virtual operator bool();
|
virtual operator bool();
|
||||||
|
|
||||||
@ -76,8 +81,7 @@ public:
|
|||||||
uint16_t remotePort();
|
uint16_t remotePort();
|
||||||
IPAddress localIP();
|
IPAddress localIP();
|
||||||
uint16_t localPort();
|
uint16_t localPort();
|
||||||
bool getNoDelay();
|
|
||||||
void setNoDelay(bool nodelay);
|
|
||||||
static void setLocalPortStart(uint16_t port) { _localPort = port; }
|
static void setLocalPortStart(uint16_t port) { _localPort = port; }
|
||||||
|
|
||||||
size_t availableForWrite();
|
size_t availableForWrite();
|
||||||
@ -96,6 +100,24 @@ public:
|
|||||||
uint8_t getKeepAliveCount () const;
|
uint8_t getKeepAliveCount () const;
|
||||||
void disableKeepAlive () { keepAlive(0, 0, 0); }
|
void disableKeepAlive () { keepAlive(0, 0, 0); }
|
||||||
|
|
||||||
|
// default NoDelay=False (Nagle=True=!NoDelay)
|
||||||
|
// Nagle is for shortly delaying outgoing data, to send less/bigger packets
|
||||||
|
// Nagle should be disabled for telnet-like/interactive streams
|
||||||
|
// Nagle is meaningless/ignored when Sync=true
|
||||||
|
static void setDefaultNoDelay (bool noDelay);
|
||||||
|
static bool getDefaultNoDelay ();
|
||||||
|
bool getNoDelay() const;
|
||||||
|
void setNoDelay(bool nodelay);
|
||||||
|
|
||||||
|
// default Sync=false
|
||||||
|
// When sync is true, all writes are automatically flushed.
|
||||||
|
// This is slower but also does not allocate
|
||||||
|
// temporary memory for sending data
|
||||||
|
static void setDefaultSync (bool sync);
|
||||||
|
static bool getDefaultSync ();
|
||||||
|
bool getSync() const;
|
||||||
|
void setSync(bool sync);
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
|
|
||||||
static int8_t _s_connected(void* arg, void* tpcb, int8_t err);
|
static int8_t _s_connected(void* arg, void* tpcb, int8_t err);
|
||||||
|
@ -271,12 +271,12 @@ uint8_t WiFiClientSecure::connected()
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
void WiFiClientSecure::stop()
|
bool WiFiClientSecure::stop(unsigned int maxWaitMs)
|
||||||
{
|
{
|
||||||
if (_ssl) {
|
if (_ssl) {
|
||||||
_ssl->stop();
|
_ssl->stop();
|
||||||
}
|
}
|
||||||
WiFiClient::stop();
|
return WiFiClient::stop(maxWaitMs);
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool parseHexNibble(char pb, uint8_t* res)
|
static bool parseHexNibble(char pb, uint8_t* res)
|
||||||
|
@ -51,7 +51,7 @@ public:
|
|||||||
int read() override;
|
int read() override;
|
||||||
int peek() override;
|
int peek() override;
|
||||||
size_t peekBytes(uint8_t *buffer, size_t length) override;
|
size_t peekBytes(uint8_t *buffer, size_t length) override;
|
||||||
void stop() override;
|
bool stop(unsigned int maxWaitMs = 0) override;
|
||||||
|
|
||||||
bool setCACert(const uint8_t* pk, size_t size);
|
bool setCACert(const uint8_t* pk, size_t size);
|
||||||
bool setCertificate(const uint8_t* pk, size_t size);
|
bool setCertificate(const uint8_t* pk, size_t size);
|
||||||
|
@ -175,23 +175,19 @@ void WiFiClientSecure::setBufferSizes(int recv, int xmit) {
|
|||||||
_iobuf_out_size = xmit;
|
_iobuf_out_size = xmit;
|
||||||
}
|
}
|
||||||
|
|
||||||
void WiFiClientSecure::stop() {
|
bool WiFiClientSecure::stop(unsigned int maxWaitMs) {
|
||||||
flush();
|
bool ret = WiFiClient::stop(maxWaitMs); // calls our virtual flush()
|
||||||
if (_client) {
|
|
||||||
_client->wait_until_sent();
|
|
||||||
_client->abort();
|
|
||||||
}
|
|
||||||
WiFiClient::stop();
|
|
||||||
// Only if we've already connected, clear the connection options
|
// Only if we've already connected, clear the connection options
|
||||||
if (_handshake_done) {
|
if (_handshake_done) {
|
||||||
_clearAuthenticationSettings();
|
_clearAuthenticationSettings();
|
||||||
}
|
}
|
||||||
_freeSSL();
|
_freeSSL();
|
||||||
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
void WiFiClientSecure::flush() {
|
bool WiFiClientSecure::flush(unsigned int maxWaitMs) {
|
||||||
(void) _run_until(BR_SSL_SENDAPP);
|
(void) _run_until(BR_SSL_SENDAPP);
|
||||||
WiFiClient::flush();
|
return WiFiClient::flush(maxWaitMs);
|
||||||
}
|
}
|
||||||
|
|
||||||
int WiFiClientSecure::connect(IPAddress ip, uint16_t port) {
|
int WiFiClientSecure::connect(IPAddress ip, uint16_t port) {
|
||||||
|
@ -55,8 +55,8 @@ class WiFiClientSecure : public WiFiClient {
|
|||||||
int read() override;
|
int read() override;
|
||||||
int peek() override;
|
int peek() override;
|
||||||
size_t peekBytes(uint8_t *buffer, size_t length) override;
|
size_t peekBytes(uint8_t *buffer, size_t length) override;
|
||||||
void stop() override;
|
bool flush(unsigned int maxWaitMs = 0) override;
|
||||||
void flush() override;
|
bool stop(unsigned int maxWaitMs = 0) override;
|
||||||
|
|
||||||
// Don't validate the chain, just accept whatever is given. VERY INSECURE!
|
// Don't validate the chain, just accept whatever is given. VERY INSECURE!
|
||||||
void setInsecure() {
|
void setInsecure() {
|
||||||
|
@ -88,11 +88,16 @@ void WiFiServer::begin(uint16_t port) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void WiFiServer::setNoDelay(bool nodelay) {
|
void WiFiServer::setNoDelay(bool nodelay) {
|
||||||
_noDelay = nodelay;
|
_noDelay = nodelay? _ndTrue: _ndFalse;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool WiFiServer::getNoDelay() {
|
bool WiFiServer::getNoDelay() {
|
||||||
return _noDelay;
|
switch (_noDelay)
|
||||||
|
{
|
||||||
|
case _ndFalse: return false;
|
||||||
|
case _ndTrue: return true;
|
||||||
|
default: return WiFiClient::getDefaultNoDelay();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool WiFiServer::hasClient() {
|
bool WiFiServer::hasClient() {
|
||||||
@ -106,7 +111,7 @@ WiFiClient WiFiServer::available(byte* status) {
|
|||||||
if (_unclaimed) {
|
if (_unclaimed) {
|
||||||
WiFiClient result(_unclaimed);
|
WiFiClient result(_unclaimed);
|
||||||
_unclaimed = _unclaimed->next();
|
_unclaimed = _unclaimed->next();
|
||||||
result.setNoDelay(_noDelay);
|
result.setNoDelay(getNoDelay());
|
||||||
DEBUGV("WS:av\r\n");
|
DEBUGV("WS:av\r\n");
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
@ -43,7 +43,7 @@ protected:
|
|||||||
|
|
||||||
ClientContext* _unclaimed;
|
ClientContext* _unclaimed;
|
||||||
ClientContext* _discarded;
|
ClientContext* _discarded;
|
||||||
bool _noDelay = false;
|
enum { _ndDefault, _ndFalse, _ndTrue } _noDelay = _ndDefault;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
WiFiServer(IPAddress addr, uint16_t port);
|
WiFiServer(IPAddress addr, uint16_t port);
|
||||||
|
@ -31,11 +31,14 @@ extern "C" void esp_schedule();
|
|||||||
|
|
||||||
#include "DataSource.h"
|
#include "DataSource.h"
|
||||||
|
|
||||||
|
bool getDefaultPrivateGlobalSyncValue ();
|
||||||
|
|
||||||
class ClientContext
|
class ClientContext
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
ClientContext(tcp_pcb* pcb, discard_cb_t discard_cb, void* discard_cb_arg) :
|
ClientContext(tcp_pcb* pcb, discard_cb_t discard_cb, void* discard_cb_arg) :
|
||||||
_pcb(pcb), _rx_buf(0), _rx_buf_offset(0), _discard_cb(discard_cb), _discard_cb_arg(discard_cb_arg), _refcnt(0), _next(0)
|
_pcb(pcb), _rx_buf(0), _rx_buf_offset(0), _discard_cb(discard_cb), _discard_cb_arg(discard_cb_arg), _refcnt(0), _next(0),
|
||||||
|
_sync(::getDefaultPrivateGlobalSyncValue())
|
||||||
{
|
{
|
||||||
tcp_setprio(pcb, TCP_PRIO_MIN);
|
tcp_setprio(pcb, TCP_PRIO_MIN);
|
||||||
tcp_arg(pcb, this);
|
tcp_arg(pcb, this);
|
||||||
@ -44,7 +47,7 @@ public:
|
|||||||
tcp_err(pcb, &_s_error);
|
tcp_err(pcb, &_s_error);
|
||||||
tcp_poll(pcb, &_s_poll, 1);
|
tcp_poll(pcb, &_s_poll, 1);
|
||||||
|
|
||||||
// not enabled by default for 2.4.0
|
// keep-alive not enabled by default
|
||||||
//keepAlive();
|
//keepAlive();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -159,7 +162,7 @@ public:
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool getNoDelay()
|
bool getNoDelay() const
|
||||||
{
|
{
|
||||||
if(!_pcb) {
|
if(!_pcb) {
|
||||||
return false;
|
return false;
|
||||||
@ -167,17 +170,17 @@ public:
|
|||||||
return tcp_nagle_disabled(_pcb);
|
return tcp_nagle_disabled(_pcb);
|
||||||
}
|
}
|
||||||
|
|
||||||
void setTimeout(int timeout_ms)
|
void setTimeout(int timeout_ms)
|
||||||
{
|
{
|
||||||
_timeout_ms = timeout_ms;
|
_timeout_ms = timeout_ms;
|
||||||
}
|
}
|
||||||
|
|
||||||
int getTimeout()
|
int getTimeout() const
|
||||||
{
|
{
|
||||||
return _timeout_ms;
|
return _timeout_ms;
|
||||||
}
|
}
|
||||||
|
|
||||||
uint32_t getRemoteAddress()
|
uint32_t getRemoteAddress() const
|
||||||
{
|
{
|
||||||
if(!_pcb) {
|
if(!_pcb) {
|
||||||
return 0;
|
return 0;
|
||||||
@ -186,7 +189,7 @@ public:
|
|||||||
return _pcb->remote_ip.addr;
|
return _pcb->remote_ip.addr;
|
||||||
}
|
}
|
||||||
|
|
||||||
uint16_t getRemotePort()
|
uint16_t getRemotePort() const
|
||||||
{
|
{
|
||||||
if(!_pcb) {
|
if(!_pcb) {
|
||||||
return 0;
|
return 0;
|
||||||
@ -195,7 +198,7 @@ public:
|
|||||||
return _pcb->remote_port;
|
return _pcb->remote_port;
|
||||||
}
|
}
|
||||||
|
|
||||||
uint32_t getLocalAddress()
|
uint32_t getLocalAddress() const
|
||||||
{
|
{
|
||||||
if(!_pcb) {
|
if(!_pcb) {
|
||||||
return 0;
|
return 0;
|
||||||
@ -204,7 +207,7 @@ public:
|
|||||||
return _pcb->local_ip.addr;
|
return _pcb->local_ip.addr;
|
||||||
}
|
}
|
||||||
|
|
||||||
uint16_t getLocalPort()
|
uint16_t getLocalPort() const
|
||||||
{
|
{
|
||||||
if(!_pcb) {
|
if(!_pcb) {
|
||||||
return 0;
|
return 0;
|
||||||
@ -257,7 +260,7 @@ public:
|
|||||||
return size_read;
|
return size_read;
|
||||||
}
|
}
|
||||||
|
|
||||||
char peek()
|
char peek() const
|
||||||
{
|
{
|
||||||
if(!_rx_buf) {
|
if(!_rx_buf) {
|
||||||
return 0;
|
return 0;
|
||||||
@ -266,7 +269,7 @@ public:
|
|||||||
return reinterpret_cast<char*>(_rx_buf->payload)[_rx_buf_offset];
|
return reinterpret_cast<char*>(_rx_buf->payload)[_rx_buf_offset];
|
||||||
}
|
}
|
||||||
|
|
||||||
size_t peekBytes(char *dst, size_t size)
|
size_t peekBytes(char *dst, size_t size) const
|
||||||
{
|
{
|
||||||
if(!_rx_buf) {
|
if(!_rx_buf) {
|
||||||
return 0;
|
return 0;
|
||||||
@ -296,20 +299,48 @@ public:
|
|||||||
_rx_buf_offset = 0;
|
_rx_buf_offset = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
void wait_until_sent()
|
bool wait_until_sent(int max_wait_ms = WIFICLIENT_MAX_FLUSH_WAIT_MS)
|
||||||
{
|
{
|
||||||
// fix option 1 in
|
|
||||||
// https://github.com/esp8266/Arduino/pull/3967#pullrequestreview-83451496
|
// https://github.com/esp8266/Arduino/pull/3967#pullrequestreview-83451496
|
||||||
// TODO: option 2
|
// option 1 done
|
||||||
|
// option 2 / _write_some() not necessary since _datasource is always nullptr here
|
||||||
|
|
||||||
#define WAIT_TRIES_MS 10 // at most 10ms
|
if (!_pcb)
|
||||||
|
return true;
|
||||||
|
|
||||||
int tries = 1+ WAIT_TRIES_MS;
|
int loop = -1;
|
||||||
|
int prevsndbuf = -1;
|
||||||
|
max_wait_ms++;
|
||||||
|
|
||||||
while (state() == ESTABLISHED && tcp_sndbuf(_pcb) != TCP_SND_BUF && --tries) {
|
// wait for peer's acks to flush lwIP's output buffer
|
||||||
_write_some();
|
|
||||||
delay(1); // esp_ schedule+yield
|
while (1) {
|
||||||
|
|
||||||
|
// force lwIP to send what can be sent
|
||||||
|
tcp_output(_pcb);
|
||||||
|
|
||||||
|
int sndbuf = tcp_sndbuf(_pcb);
|
||||||
|
if (sndbuf != prevsndbuf) {
|
||||||
|
// send buffer has changed (or first iteration)
|
||||||
|
// we received an ack: restart the loop counter
|
||||||
|
prevsndbuf = sndbuf;
|
||||||
|
loop = max_wait_ms;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (state() != ESTABLISHED || sndbuf == TCP_SND_BUF || --loop <= 0)
|
||||||
|
break;
|
||||||
|
|
||||||
|
delay(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#ifdef DEBUGV
|
||||||
|
if (loop <= 0) {
|
||||||
|
// wait until sent: timeout
|
||||||
|
DEBUGV(":wustmo\n");
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
return max_wait_ms > 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
uint8_t state() const
|
uint8_t state() const
|
||||||
@ -321,7 +352,6 @@ public:
|
|||||||
return _pcb->state;
|
return _pcb->state;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
size_t write(const uint8_t* data, size_t size)
|
size_t write(const uint8_t* data, size_t size)
|
||||||
{
|
{
|
||||||
if (!_pcb) {
|
if (!_pcb) {
|
||||||
@ -379,6 +409,16 @@ public:
|
|||||||
return isKeepAliveEnabled()? _pcb->keep_cnt: 0;
|
return isKeepAliveEnabled()? _pcb->keep_cnt: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool getSync () const
|
||||||
|
{
|
||||||
|
return _sync;
|
||||||
|
}
|
||||||
|
|
||||||
|
void setSync (bool sync)
|
||||||
|
{
|
||||||
|
_sync = sync;
|
||||||
|
}
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
|
|
||||||
bool _is_timeout()
|
bool _is_timeout()
|
||||||
@ -418,6 +458,10 @@ protected:
|
|||||||
esp_yield();
|
esp_yield();
|
||||||
} while(true);
|
} while(true);
|
||||||
_send_waiting = 0;
|
_send_waiting = 0;
|
||||||
|
|
||||||
|
if (_sync)
|
||||||
|
wait_until_sent();
|
||||||
|
|
||||||
return _written;
|
return _written;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -427,40 +471,50 @@ protected:
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
size_t left = _datasource->available();
|
DEBUGV(":wr %d %d\r\n", _datasource->available(), _written);
|
||||||
size_t can_send = tcp_sndbuf(_pcb);
|
|
||||||
if (_pcb->snd_queuelen >= TCP_SND_QUEUELEN) {
|
bool has_written = false;
|
||||||
can_send = 0;
|
|
||||||
}
|
while (_datasource) {
|
||||||
size_t will_send = (can_send < left) ? can_send : left;
|
if (state() == CLOSED)
|
||||||
DEBUGV(":wr %d %d %d\r\n", will_send, left, _written);
|
return false;
|
||||||
bool need_output = false;
|
size_t next_chunk_size = std::min((size_t)tcp_sndbuf(_pcb), _datasource->available());
|
||||||
while( will_send && _datasource) {
|
if (!next_chunk_size)
|
||||||
size_t next_chunk =
|
|
||||||
will_send > _write_chunk_size ? _write_chunk_size : will_send;
|
|
||||||
const uint8_t* buf = _datasource->get_buffer(next_chunk);
|
|
||||||
if (state() == CLOSED) {
|
|
||||||
need_output = false;
|
|
||||||
break;
|
break;
|
||||||
}
|
const uint8_t* buf = _datasource->get_buffer(next_chunk_size);
|
||||||
err_t err = tcp_write(_pcb, buf, next_chunk, TCP_WRITE_FLAG_COPY);
|
// use TCP_WRITE_FLAG_MORE to remove PUSH flag from packet (lwIP's doc),
|
||||||
DEBUGV(":wrc %d %d %d\r\n", next_chunk, will_send, (int) err);
|
// because PUSH code implicitely disables Nagle code (see lwIP's tcp_out.c)
|
||||||
|
// Notes:
|
||||||
|
// PUSH is meant for peer, telling to give data to user app as soon as received
|
||||||
|
// PUSH "may be set" when sender has finished sending a meaningful data block
|
||||||
|
// PUSH is quite unclear in its application
|
||||||
|
// Nagle is for shortly delaying outgoing data, to send less/bigger packets
|
||||||
|
uint8_t flags = TCP_WRITE_FLAG_MORE; // do not tcp-PuSH
|
||||||
|
if (!_sync)
|
||||||
|
// user data must be copied when data are sent but not yet acknowledged
|
||||||
|
// (with sync, we wait for acknowledgment before returning to user)
|
||||||
|
flags |= TCP_WRITE_FLAG_COPY;
|
||||||
|
err_t err = tcp_write(_pcb, buf, next_chunk_size, flags);
|
||||||
|
DEBUGV(":wrc %d %d %d\r\n", next_chunk_size, _datasource->available(), (int)err);
|
||||||
if (err == ERR_OK) {
|
if (err == ERR_OK) {
|
||||||
_datasource->release_buffer(buf, next_chunk);
|
_datasource->release_buffer(buf, next_chunk_size);
|
||||||
_written += next_chunk;
|
_written += next_chunk_size;
|
||||||
need_output = true;
|
has_written = true;
|
||||||
} else {
|
} else {
|
||||||
// ERR_MEM(-1) is a valid error meaning
|
// ERR_MEM(-1) is a valid error meaning
|
||||||
// "come back later". It leaves state() opened
|
// "come back later". It leaves state() opened
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
will_send -= next_chunk;
|
|
||||||
}
|
}
|
||||||
if( need_output ) {
|
|
||||||
|
if (has_written && (_sync || tcp_nagle_disabled(_pcb)))
|
||||||
|
{
|
||||||
|
// handle no-Nagle manually because of TCP_WRITE_FLAG_MORE
|
||||||
|
// lwIP's tcp_output doc: "Find out what we can send and send it"
|
||||||
tcp_output(_pcb);
|
tcp_output(_pcb);
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
return false;
|
|
||||||
|
return has_written;
|
||||||
}
|
}
|
||||||
|
|
||||||
void _write_some_from_cb()
|
void _write_some_from_cb()
|
||||||
@ -482,14 +536,13 @@ protected:
|
|||||||
|
|
||||||
void _consume(size_t size)
|
void _consume(size_t size)
|
||||||
{
|
{
|
||||||
|
if(_pcb)
|
||||||
|
tcp_recved(_pcb, size);
|
||||||
ptrdiff_t left = _rx_buf->len - _rx_buf_offset - size;
|
ptrdiff_t left = _rx_buf->len - _rx_buf_offset - size;
|
||||||
if(left > 0) {
|
if(left > 0) {
|
||||||
_rx_buf_offset += size;
|
_rx_buf_offset += size;
|
||||||
} else if(!_rx_buf->next) {
|
} else if(!_rx_buf->next) {
|
||||||
DEBUGV(":c0 %d, %d\r\n", size, _rx_buf->tot_len);
|
DEBUGV(":c0 %d, %d\r\n", size, _rx_buf->tot_len);
|
||||||
if(_pcb) {
|
|
||||||
tcp_recved(_pcb, _rx_buf->len);
|
|
||||||
}
|
|
||||||
pbuf_free(_rx_buf);
|
pbuf_free(_rx_buf);
|
||||||
_rx_buf = 0;
|
_rx_buf = 0;
|
||||||
_rx_buf_offset = 0;
|
_rx_buf_offset = 0;
|
||||||
@ -499,9 +552,6 @@ protected:
|
|||||||
_rx_buf = _rx_buf->next;
|
_rx_buf = _rx_buf->next;
|
||||||
_rx_buf_offset = 0;
|
_rx_buf_offset = 0;
|
||||||
pbuf_ref(_rx_buf);
|
pbuf_ref(_rx_buf);
|
||||||
if(_pcb) {
|
|
||||||
tcp_recved(_pcb, head->len);
|
|
||||||
}
|
|
||||||
pbuf_free(head);
|
pbuf_free(head);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -592,7 +642,6 @@ private:
|
|||||||
|
|
||||||
DataSource* _datasource = nullptr;
|
DataSource* _datasource = nullptr;
|
||||||
size_t _written = 0;
|
size_t _written = 0;
|
||||||
size_t _write_chunk_size = 256;
|
|
||||||
uint32_t _timeout_ms = 5000;
|
uint32_t _timeout_ms = 5000;
|
||||||
uint32_t _op_start_time = 0;
|
uint32_t _op_start_time = 0;
|
||||||
uint8_t _send_waiting = 0;
|
uint8_t _send_waiting = 0;
|
||||||
@ -600,6 +649,8 @@ private:
|
|||||||
|
|
||||||
int8_t _refcnt;
|
int8_t _refcnt;
|
||||||
ClientContext* _next;
|
ClientContext* _next;
|
||||||
|
|
||||||
|
bool _sync;
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif//CLIENTCONTEXT_H
|
#endif//CLIENTCONTEXT_H
|
||||||
|
@ -119,13 +119,15 @@ int EthernetClient::peek() {
|
|||||||
return b;
|
return b;
|
||||||
}
|
}
|
||||||
|
|
||||||
void EthernetClient::flush() {
|
bool EthernetClient::flush(unsigned int maxWaitMs) {
|
||||||
|
(void)maxWaitMs;
|
||||||
::flush(_sock);
|
::flush(_sock);
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void EthernetClient::stop() {
|
bool EthernetClient::stop(unsigned int maxWaitMs) {
|
||||||
if (_sock == MAX_SOCK_NUM)
|
if (_sock == MAX_SOCK_NUM)
|
||||||
return;
|
return true;
|
||||||
|
|
||||||
// attempt to close the connection gracefully (send a FIN to other side)
|
// attempt to close the connection gracefully (send a FIN to other side)
|
||||||
disconnect(_sock);
|
disconnect(_sock);
|
||||||
@ -133,19 +135,27 @@ void EthernetClient::stop() {
|
|||||||
|
|
||||||
// wait up to a second for the connection to close
|
// wait up to a second for the connection to close
|
||||||
uint8_t s;
|
uint8_t s;
|
||||||
|
if (maxWaitMs == 0)
|
||||||
|
maxWaitMs = 1000;
|
||||||
do {
|
do {
|
||||||
s = status();
|
s = status();
|
||||||
if (s == SnSR::CLOSED)
|
if (s == SnSR::CLOSED)
|
||||||
break; // exit the loop
|
break; // exit the loop
|
||||||
delay(1);
|
delay(1);
|
||||||
} while (millis() - start < 1000);
|
} while (millis() - start < maxWaitMs);
|
||||||
|
|
||||||
|
bool ret = true;
|
||||||
|
|
||||||
// if it hasn't closed, close it forcefully
|
// if it hasn't closed, close it forcefully
|
||||||
if (s != SnSR::CLOSED)
|
if (s != SnSR::CLOSED) {
|
||||||
|
ret = false;
|
||||||
close(_sock);
|
close(_sock);
|
||||||
|
}
|
||||||
|
|
||||||
EthernetClass::_server_port[_sock] = 0;
|
EthernetClass::_server_port[_sock] = 0;
|
||||||
_sock = MAX_SOCK_NUM;
|
_sock = MAX_SOCK_NUM;
|
||||||
|
|
||||||
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
uint8_t EthernetClient::connected() {
|
uint8_t EthernetClient::connected() {
|
||||||
|
@ -20,8 +20,8 @@ public:
|
|||||||
virtual int read();
|
virtual int read();
|
||||||
virtual int read(uint8_t *buf, size_t size);
|
virtual int read(uint8_t *buf, size_t size);
|
||||||
virtual int peek();
|
virtual int peek();
|
||||||
virtual void flush();
|
virtual bool flush(unsigned int maxWaitMs = 0);
|
||||||
virtual void stop();
|
virtual bool stop(unsigned int maxWaitMs = 0);
|
||||||
virtual uint8_t connected();
|
virtual uint8_t connected();
|
||||||
virtual operator bool();
|
virtual operator bool();
|
||||||
virtual bool operator==(const bool value) { return bool() == value; }
|
virtual bool operator==(const bool value) { return bool() == value; }
|
||||||
|
Loading…
x
Reference in New Issue
Block a user