Skip to content

ESP8266WebServer responds sluggy to some client after commit 83a8076db87b77de6a7b500c18b85057e38ed117 #5173

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
jjsuwa opened this issue Sep 27, 2018 · 5 comments · Fixed by #5176
Assignees

Comments

@jjsuwa
Copy link
Contributor

jjsuwa commented Sep 27, 2018

Example:
HTTP server (esp8266/Arduino simple HTTP server w/mDNS):

#include <ESP8266WiFi.h>
#include <ESP8266mDNS.h>
#include <ESP8266WebServer.h>

#define WIFI_SSID     "YOUR-WIFI-SSID"
#define WIFI_PASSWORD "YOUR-WIFI-PASSWORD"
#define MDNS_HOSTNAME "avahi"

static ESP8266WebServer webServer(80);

void setup() {
  Serial.begin(115200);
  delay(250);
  Serial.print(F("\n\nconnecting to WiFi"));
  WiFi.persistent(false);
  // WiFiClient::setDefaultNoDelay(false);
  // WiFiClient::setDefaultSync(false);
  WiFi.mode(WIFI_STA);
  WiFi.begin(String(F(WIFI_SSID)).c_str(), String(F(WIFI_PASSWORD)).c_str());
  for (; ; delay(500)) {
    if (WiFi.status() == WL_CONNECTED)
      break;
    Serial.print('.');
  }
  Serial.print(F(", done.\n"));  MDNS.begin(String(F(MDNS_HOSTNAME)).c_str());
  MDNS.addService(F("http"), F("tcp"), 80);
  webServer.onNotFound([]() {
    webServer.send(404);
  });
  webServer.on(F("/"), HTTP_GET, []() {
    webServer.sendHeader(F("Location"), String(F("http://" MDNS_HOSTNAME ".local/index.html")));
    webServer.send(302);
  });
  webServer.on(F("/index.html"), HTTP_GET, []() {
    webServer.send(200, F("text/html"), F("<!DOCTYPE html><html>"
                                          "<head><meta http-equiv=\"Content-Type\" content=\"text/html; charset=utf-8\"/><title>WiFi HTTP server test</title></head>"
                                          "<body><h1>WiFi HTTP server test</h1><p>hello world</p></body>"
                                          "</html>"));
  });
  webServer.begin();
}

void loop() {
  webServer.handleClient();
}

and 2 equivalent HTTP clients... (C# Windows 7/ndp20):

using System;
using System.Diagnostics;
using System.IO;
using System.Net;

namespace httprequest2avahi
{
    internal static class Program
    {
        public static void Main(String[] args)
        {
            String url = String.Format("http://{0}/index.html", Dns.GetHostAddresses("avahi.local")[0]);
            Stopwatch sw = new Stopwatch();
            for(Int32 i = 1; ; ++i)
            {
                HttpWebRequest req = (HttpWebRequest)WebRequest.Create(url);
                req.Timeout = 60000;
                req.ReadWriteTimeout = 60000;
                req.KeepAlive = false;
                req.Pipelined = false;
                sw.Reset();
                sw.Start();
                using(HttpWebResponse res = (HttpWebResponse)req.GetResponse())
                {
                    sw.Stop();
                    Double d = sw.Elapsed.TotalSeconds;
                    if(d > 1)
                        Console.WriteLine("[{0}] req.GetResponse(): {1} seconds", DateTime.Now, d);
                    if(res.StatusCode != HttpStatusCode.OK)
                        break;
                    using(Stream s = res.GetResponseStream())
                    using(StreamReader sr = new StreamReader(s))
                        sr.ReadToEnd();
                }
                Console.Write("connected {0} times\r", i);
            }
        }
    }
}

(Python3 Windows 7 or Raspberry Pi 3B):

#!/usr/bin/env python3

import http.client
import itertools
import socket
from datetime import datetime

if __name__ == "__main__":
    conn = http.client.HTTPConnection(socket.gethostbyname("avahi.local"), 80, timeout=60)
    for i in itertools.count(1):
        t = datetime.now()
        conn.request("GET", "/index.html")
        d = (datetime.now() - t).total_seconds()
        if d > 1:
            print(" [{}] conn.request(): {} seconds\n".format(t, d))
        with conn.getresponse() as res:
            if res.status != 200:
                break
            res.read()
        print("connected {} times\r".format(i), end="")

On commit 88bd26b (or earlier),

  • both 2 abovementioned clients receive responses well (100+ per second)

However on commit 83a8076 ~ 7c91eee,

  • C# client receives suddenly slow (2~3 per second), but Pythons well
  • WiFiClient::setDefaultNoDelay(true) recovers the performance

Moreover on commit 775eb9b,

  • C# client performance degradation like the above
  • WiFiClient::setDefaultNoDelay(true) is ineffective now

Additional Info:
Patched with

   });
   webServer.on(F("/index.html"), HTTP_GET, []() {
-    webServer.send(200, F("text/html"), F("<!DOCTYPE html><html>"
-                                          "<head><meta http-equiv=\"Content-Type\" content=\"text/html; charset=utf-8\"/><title>WiFi HTTP server test</title></head>"
-                                          "<body><h1>WiFi HTTP server test</h1><p>hello world</p></body>"
-                                          "</html>"));
+#define BLOB_SIZE 3996
+    static const char blob[BLOB_SIZE] PROGMEM = { 0 };
+    webServer.send_P(200, PSTR("application/octet-stream"), blob, BLOB_SIZE);
   });
   webServer.begin();

and then,

  • #define BLOB_SIZE 3996 is OK but 3995 isn't. Related to MSS or # of packet? (lwIP Variant: "v2 Low Memory")
@d-a-v
Copy link
Collaborator

d-a-v commented Sep 27, 2018

I will not be able to easily run C#,
can you try this in libraries/ESP8266WiFi/src/include/ClientContext.h L493:
change
uint8_t flags = TCP_WRITE_FLAG_MORE; // do not tcp-PuSH
to
uint8_t flags = 0; // TCP_WRITE_FLAG_MORE; // do not tcp-PuSH

@jjsuwa
Copy link
Contributor Author

jjsuwa commented Sep 27, 2018

@d-a-v - can you try this ~

Bingo! It recovers C# client performance w/o setDefaultNoDelay(true), and python one also works well :)

@d-a-v
Copy link
Collaborator

d-a-v commented Sep 27, 2018

Thanks !
Let me some time to try to set the PuSH flag only in the last write, as stated in this interesting explanation.

edit: that, because I suspect this flag to break Nagle, I need to check again
edit2: we've been here before: #1577

@d-a-v
Copy link
Collaborator

d-a-v commented Sep 27, 2018

Can you please retry with C# and this:

            uint8_t flags = 0;
            if (next_chunk_size < _datasource->available())
                flags |= TCP_WRITE_FLAG_MORE; // do not tcp-PuSH

d-a-v added a commit to d-a-v/Arduino that referenced this issue Sep 27, 2018
@jjsuwa
Copy link
Contributor Author

jjsuwa commented Sep 27, 2018

@d-a-v - Can you please retry with C# and this

LGTM.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging a pull request may close this issue.

2 participants