Web Server - Can't connect more than five times

Inquisitor
Posts: 22
Joined: Thu Dec 14, 2017 10:53 am
Contact:

Web Server - Can't connect more than five times

Postby Inquisitor » Mon Nov 01, 2021 10:12 pm

I'm working on a native SDK web server (no lwIP) and have it working except... after the 5th connection, it will accept no more. I searched through the documentation, the available source code and examples. This sounds like I'm running into the espconn_tcp_get_max_con(). I am...

  1. receiving the request
  2. responding with a simple 404
  3. see the browser display the 404
  4. waiting for the espconn_sent_callback()
  5. doing a espconn_disconnect() outside of the callback,
  6. doing a espconn_delete()
This all works fine the first 5 requests... but none after. Doing an lwIP version web server, there are methods to tell the low level that to hold/send more connections... tcp_backlog_delayed() / tcp_backlog_accepted(). There doesn't seem to be an equivalent in the native methods. Here is a rudimentary, single request at a time, test web server that illustrates the problem. It's written for the Arduino IDE, but is easily ported the other way if needed.

Code: Select all

#include <user_interface.h>
#include <espconn.h>

#define SSID "InqMakers-Guest"
#define PW "Inquisitive"
#define HOST "wp"

void setup()
{
   Serial.begin(115200);
   while(!Serial) { Serial.print("."); delay(100); }
   delay(2000);
   
    // This is just cook-book Station connection stuff.
    setupWiFi();
   
    // Setup and start web server listener
    static espconn listen;
    os_memset(&listen, 0, sizeof(espconn));
    listen.type = ESPCONN_TCP;
    listen.state = ESPCONN_NONE;   
    listen.proto.tcp = new esp_tcp();   
    listen.proto.tcp->local_port = 80;
   
    espconn_regist_connectcb(&listen, onConnection); 
    // This appears to be an error handler.  Haven't seen it hit yet.
    espconn_regist_reconcb(&listen, onError);
       
    printIfErr(espconn_accept(&listen), "listen");
   
   Serial.println("Ready");
}

espconn* dis = NULL;
u32 last;

void loop()
{
    // The only thing we do in the loop is do the final disconnect since
    // we can't do it in the onSent() callback.
    if (dis)
    {
        u32 now = micros();
        Serial.printf("disconnect = %u us\n", now - last);
        last = now;
       
        s8 rtn;
        if ((rtn = espconn_disconnect(dis)))
        {
            Serial.printf("ERR - disconnect = %d\n", rtn);
            if ((rtn = espconn_abort(dis)))
                Serial.printf("ERR - abort = %d\n", rtn);
        }
        if ((rtn = espconn_delete(dis)))
            Serial.printf("ERR - delete = %d\n", rtn);
        dis = NULL;
    }
}

void onConnection(void* c)
{
    espconn* conn = reinterpret_cast<espconn*>(c);
       
    // Incoming connection
    last = micros();
    Serial.printf("\nonConnection\n");   
    espconn_regist_recvcb(conn, onReceive);
    espconn_regist_sentcb(conn, onSent);
    espconn_regist_disconcb(conn, onDisconnect);   

    espconn_set_opt(conn, ESPCONN_REUSEADDR | ESPCONN_NODELAY);
    // We'll use our own buffer instead of their 2920 buffer
    espconn_clear_opt(conn, ESPCONN_COPY);
}

void onError(void* conn, s8 err)
{
    Serial.printf("ERR=%d\n", err);
}

void onReceive(void* c, char* buf, u16 length)
{
    espconn* conn = reinterpret_cast<espconn*>(c);

    u32 now = micros();
    Serial.printf("onReceive = %u us\n", now - last);
    last = now;
   
    static char buffer[536];

    // Print out the request just to make sure its what we think it is.
    memcpy(buffer, buf, length);
    *(buffer + length) = 0;
    // Serial.print(buffer);
   
    // We don't care what's in there since we'll send back a 404 anyway.
    sprintf(buffer, "HTTP/1.1 404 Not Found\r\nServer: InqPortal/5.0\r\n",
        "Content-type: text/html\r\nPragma: no-cache\r\n\r\n");

    printIfErr(espconn_send(conn, (u8*)buffer, strlen(buffer)), "send");
   
    now = micros();
    Serial.printf("sent = %u us\n", now - last);
    last = now;
}

void onSent(void* c)
{
    u32 now = micros();
    Serial.printf("onSent = %u us\n", now - last);
    last = now;
    // Manual says we can't call disconnect in callback.  We'll use
    // cheap way of just letting the loop() do it.
    dis = reinterpret_cast<espconn*>(c);
;
}

void onDisconnect(void* c)
{
    Serial.printf("onDisconnect \n");
}

void printIfErr(s8 esp, const char* msg)
{
    if (esp)
        Serial.printf("ERR - %s = %d\n", msg, esp);
}

void setupWiFi()
{
    Serial.print("Connecting to your router");

    wifi_set_opmode(STATION_MODE);
   
    station_config sc;
    os_memset(&sc, 0, sizeof(station_config));
    sc.bssid_set = 0;
    os_memcpy(&sc.ssid, SSID, 32);
    os_memcpy(&sc.password, PW, 64);   
    wifi_station_set_config_current(&sc);

    wifi_station_set_hostname(HOST);

    wifi_station_connect();
    while (wifi_station_get_connect_status() != STATION_GOT_IP)
    {
        Serial.print(".");
        delay(1000);
    }
    ip_info   info;
    wifi_get_ip_info(STATION_IF, &info);
    Serial.printf("\nBrowse to (http://%s/index.html) or (http://" IPSTR
        "/index.html)\n",
        HOST, IP2STR(&(info.ip)));
}
3 lines of code = App/Web Server w/ GUI Admin, File Mngr, OTA, AP Mgr, Perf Metrics, WebSocket Comms, App API, All running on ESP8266... Even usable on ESP-01S
https://www.esp8266.com/viewtopic.php?f=11&t=23535
https://InqOnThat.com

Inquisitor
Posts: 22
Joined: Thu Dec 14, 2017 10:53 am
Contact:

More information...

Postby Inquisitor » Thu Nov 04, 2021 10:23 pm

I've made some modifications to the above test web server to serve out a web page with JavaScript that will cause a reload every 0.5 seconds. I've also tried it with older versions of the SDK.

  1. I notice that the espconn_regist_disconcb() callback NEVER gets called.
  2. I tried older development versions of the SDK
    1. Espressif SDK version 1.5.3(aec24ac9) Worked! - It continually re-serves the same page well into the thousands without issue and without memory leaks. This was downloaded in Arduino ESP8266 library 2.3.0.
    2. Subsequent versions do not work.
    3. Espressif SDK version 2.2.2-dev(38a443e) does not work after serving the file five times. - This was downloaded in the latest Arduino ESP8266 library 3.0.2.

Code: Select all

extern "C"
{
  #include "user_interface.h"
  #include "espconn.h"
}

#define SSID "InqMakers-Guest"
#define PW "Inquisitive"
#define HOST "wp"

const char* PAGE = "<!DOCTYPE html><html><head><title>Native Server Test</title> <meta name='viewport' content='width=device-width, initial-scale=1'><script>window.addEventListener('load',()=>{ setTimeout(()=>{location.reload();},500);},false);</script></head><body>  <h1>Welcome to my Native Server Test</h1></body></html>";

void setup()
{
   Serial.begin(115200);
   while(!Serial) { Serial.print("."); delay(100); }
   delay(2000);
   
    // This is just cook-book Station connection stuff.
    Serial.printf("\n\nSDK version: %s\n", system_get_sdk_version());

    setupWiFi();
   
    // Setup and start web server listener
    static espconn listen;
    os_memset(&listen, 0, sizeof(espconn));
    listen.type = ESPCONN_TCP;
    listen.state = ESPCONN_NONE;   
    listen.proto.tcp = new esp_tcp();   
    listen.proto.tcp->local_port = 80;
   
    espconn_regist_connectcb(&listen, onConnection); 
    // This appears to be an error handler.  Haven't seen it hit yet.
    espconn_regist_reconcb(&listen, onError);
       
    printIfErr(espconn_accept(&listen), "listen");
   
   Serial.println("Ready");
}

espconn* dis = NULL;
u32 last;
u32 cnt = 0;

void loop()
{
    // The only thing we do in the loop is do the final disconnect since
    // we can't do it in the onSent() callback.
    if (dis)
    {
        u32 now = micros();
        Serial.printf("dis=%uus ", now - last);
        last = now;
       
        s8 rtn;
        if ((rtn = espconn_disconnect(dis)))
        {
            Serial.printf("ERR - disconnect = %d\n", rtn);
            if ((rtn = espconn_abort(dis)))
                Serial.printf("ERR - abort = %d\n", rtn);
        }
        if ((rtn = espconn_delete(dis)))
            Serial.printf("del=%d ", rtn);
        dis = NULL;
    }
}

void onConnection(void* c)
{
    espconn* conn = reinterpret_cast<espconn*>(c);
       
    // Incoming connection
    last = micros();
    Serial.printf("\nconn(%u), ", cnt++);   
    espconn_regist_recvcb(conn, onReceive);
    espconn_regist_sentcb(conn, onSent);
    espconn_regist_disconcb(conn, onDisconnect);   

    espconn_set_opt(conn, ESPCONN_REUSEADDR | ESPCONN_NODELAY);
    // We'll use our own buffer instead of their 2920 buffer
    //espconn_clear_opt(conn, ESPCONN_COPY | ESPCONN_KEEPALIVE);
}

void onError(void* conn, s8 err)
{
    Serial.printf("ERR=%d\n", err);
}

void onReceive(void* c, char* buf, u16 length)
{
    espconn* conn = reinterpret_cast<espconn*>(c);

    u32 now = micros();
    Serial.printf("rcv=%uus ", now - last);
    last = now;
   
    static char buffer[1024];

    // Print out the request just to make sure its what we think it is.
    memcpy(buffer, buf, length);
    *(buffer + length) = 0;
    // Serial.print(buffer);
   
    // We'll send back a page that keeps reloading,
    // so we can check for errors and/or memory leaks.
    sprintf(buffer, "HTTP/1.1 200 OK\r\n"
        "Server: InqPortal/5.0\r\n"
        "Content-Length: %d\r\n"
        "Content-type: text/html\r\n"
        "Pragma: no-cache\r\n\r\n%s", strlen(PAGE), PAGE);
    printIfErr(espconn_sent(conn, (u8*)buffer, strlen(buffer)), "send");
   
    now = micros();
    Serial.printf("snd=%uus ", now - last);
    last = now;
}

void onSent(void* c)
{
    espconn* conn = reinterpret_cast<espconn*>(c);
   
    u32 now = micros();
    Serial.printf("snt=%uus ", now - last);
    last = now;
    // Manual says we can't call disconnect in callback.  We'll use
    // cheap way of just letting the loop() do it.
    dis = conn;
}

void onDisconnect(void* c)
{
    u32 now = micros();
    Serial.printf("dis(%p)=%uus mem=%u",
        c, now - last, system_get_free_heap_size());
    last = now;   
}

void printIfErr(s8 esp, const char* msg)
{
    if (esp)
        Serial.printf("ERR - %s = %d\n", msg, esp);
}

void setupWiFi()
{
    Serial.print("\nConnecting to your router");

    wifi_set_opmode(STATION_MODE);
   
    station_config sc;
    os_memset(&sc, 0, sizeof(station_config));
    sc.bssid_set = 0;
    os_memcpy(&sc.ssid, SSID, 32);
    os_memcpy(&sc.password, PW, 64);   
    wifi_station_set_config_current(&sc);

    wifi_station_set_hostname(HOST);

    wifi_station_connect();
    while (wifi_station_get_connect_status() != STATION_GOT_IP)
    {
        Serial.print(".");
        delay(1000);
    }
    ip_info   info;
    wifi_get_ip_info(STATION_IF, &info);
    Serial.printf("\nBrowse to (http://%s/index.html) or (http://" IPSTR
        "/index.html)\n",
        HOST, IP2STR(&(info.ip)));
}
3 lines of code = App/Web Server w/ GUI Admin, File Mngr, OTA, AP Mgr, Perf Metrics, WebSocket Comms, App API, All running on ESP8266... Even usable on ESP-01S
https://www.esp8266.com/viewtopic.php?f=11&t=23535
https://InqOnThat.com

Inquisitor
Posts: 22
Joined: Thu Dec 14, 2017 10:53 am
Contact:

Re: Web Server - Can't connect more than five times (SOLVED)

Postby Inquisitor » Sun Nov 07, 2021 3:37 am

Well... kind of solved.

I was wondering why no one had any suggestions. Especially since I broached this problem two years ago in https://bbs.espressif.com/viewtopic.php?f=7&t=51435. I felt certain that if the espconn_regist_disconcb() callback wasn't getting fired there would be a lot more people besides me complaining. This was my first clue... that it had something to do with the Arduino IDE libraries.

Well... I took the last couple of days to research the Internet about doing native development on Windows. The last time I looked one had to use a Linux VM and there was just too much learning curve and too many ways for it to go sideways on me. This time I found PlatformIO and having used Visual Studio for twenty-five+ years, I felt at home and the tutorials got me up and running promptly. I converted the code above to a Native C version and it worked first time!!!

So there is something amiss when trying to do a Native SDK under the Arduino IDE.
3 lines of code = App/Web Server w/ GUI Admin, File Mngr, OTA, AP Mgr, Perf Metrics, WebSocket Comms, App API, All running on ESP8266... Even usable on ESP-01S
https://www.esp8266.com/viewtopic.php?f=11&t=23535
https://InqOnThat.com

Who is online

Users browsing this forum: No registered users and 186 guests