Page 1 of 1

recv returns errno 104 / ECONNRESET after 4320 bytes

Posted: Mon Aug 02, 2021 2:51 pm
by priisen
Hi All,

I have an issue where my code connects a TCP socket to a web server then fetches some data with recv, but it eventually fails returning errno 104 (ECONNRESET) once it has read 4320 bytes.

The application is for connecting to a UPnP IGD and attempting to add a port mapping and mostly works up until it needs to read a file from the IGD that happens to be bigger than 4320 bytes. The code is fairly portable and I can compile the application on Linux and it runs fine, including fetching files bigger than 4320 bytes.

4320 happens to be 3 times the default maximum segment size (MSS) of 1440, so I'm hoping this is just a configuration issue that can be fixed by changing some settings. I tried enabling some LWiP debug output but it made matters worse and caused ECONNRESET errors on files smaller than 4320 bytes.

Before the app receives the ECONNRESET, the debug output shows
tcp_slowtmr: no active pcbs
then a short while later
lwip_recv_tcp: netconn_recv err=-7, pbuf=0x0
then
lwip_recv_tcp: netconn_recv err=-14, pbuf=0x1
and finally
lwip_recv_tcp: p == NULL, error is "Connection reset."!
.

I dug through the code and found that
no active pcbs
is related to LWIP_MAX_LISTENING_TCP which I increased from 16 to 32 but it had no effect.

The function that fetches files from the IGD (which works on Linux) is as follows:
  1. struct http_req {
  2.     int port, path_offset, len;
  3.     char host[16], buf[1];
  4. };
  5.  
  6. void http_req_run(struct http_req *q, char *body, int blen, http_req_f *heard, void *ctx)
  7. {
  8.     if (!q)
  9.         return;
  10.  
  11.     struct sockaddr_in sa = {0};
  12.     sa.sin_family = AF_INET;
  13.     sa.sin_port = htons(q->port);
  14.     inet_aton(q->host, &sa.sin_addr);
  15.  
  16.     ESP_LOGD("UPNP", "TX %s%s", q->buf, body ? body : "");
  17.     int s = socket(AF_INET, SOCK_STREAM, IPPROTO_IP), got = 0, total = 0;
  18.     if (s < 0)
  19.         ESP_LOGE("UPNP", "socket error %i", errno);
  20.     else if (connect(s, (struct sockaddr*)&sa, sizeof(sa)) < 0)
  21.         ESP_LOGE("UPNP", "connect error %i", errno);
  22.     else if (send(s, q->buf, q->len, 0) < 0)
  23.         ESP_LOGE("UPNP", "send header error %i", errno);
  24.     else if (send(s, "\r\n", 2, 0) < 0)
  25.         ESP_LOGE("UPNP", "send CRLF error %i", errno);
  26.     else if (body && send(s, body, blen, 0) < 0)
  27.         ESP_LOGE("UPNP", "send body error %i", errno);
  28.     else while ((got = recv(s, q->buf, q->len, 0)) > 0) {
  29.         q->buf[got] = 0; ESP_LOGD("UPNP", "RX %i %s", got, q->buf);
  30.         heard(ctx, q->buf, got);
  31.         total += got;
  32.     }
  33.     if (got < 0)
  34.         ESP_LOGE("UPNP", "http_req_run recv %i, read %i bytes", errno, total);
  35.  
  36.     close(s);
  37. }
I've attached the debug output for runs with and without the LWiP socket and TCP debug enabled. The latter only fails when trying to fetch the service description from the IGD. The former seems to fail for any fetch from the IGD. Any ideas what I need to change so this works on ESP32?


Cheers
Paul

Re: recv returns errno 104 / ECONNRESET after 4320 bytes

Posted: Fri Aug 06, 2021 11:48 am
by priisen
Update:

Bumping LWIP_TCPIP_RECVMBOX_SIZE up to 48 would receive 7200 bytes (5 x MSS) before failing.
Bumping LWIP_TCP_RECVMBOX_SIZE to 12 alone did nothing.
Bumping LWIP_TCP_RECVMBOX_SIZE to 12 and enabling LWIP_STATS made it work sometimes.


Cheers
Paul