arduino-libraries / Ethernet

Ethernet Library for Arduino
http://arduino.cc/
252 stars 253 forks source link

Add non-blocking alternative to EthernetClient::connect() #222

Open mghie opened 1 year ago

mghie commented 1 year ago

I have a program which needs to always update outputs in a timely manner, even when calling EthernetClient::connect() (which can block for the number of milliseconds set with setConnectionTimeout()).

I have modified the current code:

int EthernetClient::connect(IPAddress ip, uint16_t port)
{
    if (_sockindex < MAX_SOCK_NUM) {
        if (Ethernet.socketStatus(_sockindex) != SnSR::CLOSED) {
            Ethernet.socketDisconnect(_sockindex); // TODO: should we call stop()?
        }
        _sockindex = MAX_SOCK_NUM;
    }
#if defined(ESP8266) || defined(ESP32)
    if (ip == IPAddress((uint32_t)0) || ip == IPAddress(0xFFFFFFFFul)) return 0;
#else
    if (ip == IPAddress(0ul) || ip == IPAddress(0xFFFFFFFFul)) return 0;
#endif
    _sockindex = Ethernet.socketBegin(SnMR::TCP, 0);
    if (_sockindex >= MAX_SOCK_NUM) return 0;
    Ethernet.socketConnect(_sockindex, rawIPAddress(ip), port);
    uint32_t start = millis();
    while (1) {
        uint8_t stat = Ethernet.socketStatus(_sockindex);
        if (stat == SnSR::ESTABLISHED) return 1;
        if (stat == SnSR::CLOSE_WAIT) return 1;
        if (stat == SnSR::CLOSED) return 0;
        if (millis() - start > _timeout) break;
        delay(1);
    }
    Ethernet.socketClose(_sockindex);
    _sockindex = MAX_SOCK_NUM;
    return 0;
}

to the following:

int EthernetClient::connect(IPAddress ip, uint16_t port)
{
    int ret = beginConnect(ip, port);
    if (ret >= 0) return ret;

    uint32_t start = millis();
    while (1) {
        ret = endConnect();
        if (ret >= 0) return ret;
        if (millis() - start > _timeout) break;
        delay(1);
    }
    Ethernet.socketClose(_sockindex);
    _sockindex = MAX_SOCK_NUM;
    return 0;
}

int EthernetClient::beginConnect(IPAddress ip, uint16_t port)
{
    if (_sockindex < MAX_SOCK_NUM) {
        if (Ethernet.socketStatus(_sockindex) != SnSR::CLOSED) {
            Ethernet.socketDisconnect(_sockindex); // TODO: should we call stop()?
        }
        _sockindex = MAX_SOCK_NUM;
    }
    if (ip == IPAddress(0ul) || ip == IPAddress(0xFFFFFFFFul)) return 0;
    _sockindex = Ethernet.socketBegin(SnMR::TCP, 0);
    if (_sockindex >= MAX_SOCK_NUM) return 0;
    Ethernet.socketConnect(_sockindex, rawIPAddress(ip), port);
    return -1;
}

int EthernetClient::endConnect()
{
    uint8_t stat = Ethernet.socketStatus(_sockindex);
    if (stat == SnSR::ESTABLISHED) return 1;
    if (stat == SnSR::CLOSE_WAIT) return 1;
    if (stat == SnSR::CLOSED) return 0;
    return -1;
}

Now instead of calling connect() I can call beginConnect() and then repeatedly call endConnect() during the main loop until it returns either 1 on success or 0 on failure.

If there is interest to include this into the library I can update the documentation (which is not up-to-date for connect() anyway) and create a PR. Please let me know.