Несколько подключений WiFi клиент/сервер

Я реализую HTML-веб-сервер Wi-Fi, используя библиотеку WiFiNINA, сопроцессор Adafruit Airlift ESP32 и Teensy 3.5. Это начинает работать, но у меня есть пара вопросов. Этот упрощенный код иллюстрирует первый.

#include <WiFiNINA.h>
#include "Wifi_names.h"

WiFiServer server(80);

void error (const char *msg) {
   Serial.println(msg);
   while (1) ; }

void setup(void) {
   Serial.begin(115200);
   while (!Serial) ;
   WiFi.setPins(19, 20, 21, -1, &SPI);
   if (WiFi.status() == WL_NO_SHIELD) error("no Wifi shield");
   Serial.print("ESP32 firmware version: "); Serial.println(WiFi.firmwareVersion());
   WiFi.begin(WIFI_SSID, WIFI_PASSWORD);
   while (WiFi.status() != WL_CONNECTED) ;
   Serial.print("connected, IP address is ");
   server.begin();
   Serial.println(WiFi.localIP()); }

void show_client(const char *msg, WiFiClient *pclient) {
   Serial.print(msg); Serial.print(":");
   if (*pclient) {
      Serial.print(" IP "); Serial.print(pclient->remoteIP());
      Serial.print(" port "); Serial.print(pclient->remotePort()); }
   else Serial.print("none");
   Serial.println(); }

void check_for_another_client(const char *msg) {
   WiFiClient newClient;
   newClient = server.available();
   show_client(msg, &newClient); }

void loop(void) {
   WiFiClient client;
   while (!(client = server.available())) ;
   Serial.print("\n time: "); Serial.println((float)millis()/1000);
   show_client("client 1", &client);
   check_for_another_client("client 2");
   while (client.available() == 0) ;  // ждем данных
   check_for_another_client("client 3");
   Serial.print("client 1 data read: "); Serial.println(client.read(), HEX); // прочитать данные
   check_for_another_client("client 4");
   while (client.available()) client.read(); // сбросить остальную часть ввода
   delay(1000);
   client.stop(); // прерываем соединение
}

Вывод:

ESP32 firmware version: 1.2.2
connected, IP address is 192.168.86.35

 time: 43.22
client 1: IP 192.168.86.32 port 62346
client 2: IP 192.168.86.32 port 62346
client 3:none
client 1 data read: 47
client 4:none

Вопрос: почему server. available() продолжает говорить, что у меня есть новые соединения, пока не появятся какие-то данные о соединении, которое он мне уже дал? Что делать, если второе соединение устанавливается после трехэтапного установления TCP-соединения (SYN, ACK/SYN, ACK), но до поступления каких-либо данных? Как получить это второе соединение?

Второй связанный с этим вопрос иллюстрируется трассировкой анализатора этой транзакции:

179 *REF*     192.168.86.32  192.168.86.35  TCP  66  62346 → 80 [SYN] Seq=0 Win=64240 Len=0 MSS=1460 WS=256 SACK_PERM=1
180 0.000239  192.168.86.32  192.168.86.35  TCP  66  62347 → 80 [SYN] Seq=0 Win=64240 Len=0 MSS=1460 WS=256 SACK_PERM=1
183 0.039352  192.168.86.35  192.168.86.32  TCP  58  80 → 62346 [SYN, ACK] Seq=0 Ack=1 Win=5744 Len=0 MSS=1436
184 0.039431  192.168.86.32  192.168.86.35  TCP  54  62346 → 80 [ACK] Seq=1 Ack=1 Win=64240 Len=0
185 0.039602  192.168.86.32  192.168.86.35  HTTP 488 GET / HTTP/1.1 
195 0.278592  192.168.86.35  192.168.86.32  TCP  54  80 → 62346 [ACK] Seq=1 Ack=435 Win=5310 Len=0
197 1.053036  192.168.86.35  192.168.86.32  TCP  54  80 → 62346 [FIN, ACK] Seq=1 Ack=435 Win=5310 Len=0
198 1.053146  192.168.86.32  192.168.86.35  TCP  54  62346 → 80 [ACK] Seq=435 Ack=2 Win=64240 Len=0
199 1.053600  192.168.86.32  192.168.86.35  TCP  54  62346 → 80 [FIN, ACK] Seq=435 Ack=2 Win=64240 Len=0
200 1.060248  192.168.86.35  192.168.86.32  TCP  54  80 → 62346 [ACK] Seq=2 Ack=436 Win=5309 Len=0

Запрос исходит от браузера Chrome. Как видите, он пытается установить два одновременных TCP-соединения с двух разных исходных портов, 62346 и 62347, потому что второй будет использоваться для встроенных изображений. Но WiFiMINA никогда не принимает это соединение с трехсторонним рукопожатием и никогда не дает его мне. Через три секунды Chrome повторно отправит второй пакет SYN, но пока первое соединение остается активным, WiFiMINA продолжает его игнорировать.

Как вы можете принять два одновременных TCP-соединения на разных исходных портах от одного и того же клиента? Это необходимо для Chrome.

, 👍1


1 ответ


1

Сервер.доступный() в сетевых библиотеках Arduino всегда возвращает первого подключенного клиента. Это противоречит библиотекам WiFi ESP8266 и ESP32 для Arduino. Они помечают возвращенное соединение как заявленное и больше не возвращают его. Если WiFiClient на ESP выходит за рамки, он закрывается. В библиотеках Arduino вы должны остановить () клиент, чтобы закрыть соединение и освободить «сокет».

В библиотеке Arduino Ethernet есть новая функция, еще не реализованная в библиотеках WiFi. Это server.accept(). Он помечает соединение как принятое, и следующий вызов не возвращает того же клиента. Скетч по-прежнему отвечает за вызов stop(). Использование accept() продемонстрировано в примере AdvancedChatServer библиотеки Ethernet 2.00.

Что вы можете сделать? Получите соединение (WiFiClient), обработайте запрос, отправьте ответ и остановите () соединение. Затем обработайте следующего клиента. Установите заголовок Expires, чтобы позволить браузеру кэшировать статические файлы.

,

«Сервер.доступный() в сетевых библиотеках Arduino всегда возвращает первого подключенного клиента». Похоже, это не относится к WiFiMINA. Это правда, когда я впервые вызываю его в своем примере после подключения («Клиент 2»), но затем, как только данные доступны для подключения, server.available() возвращает ноль., @Len Shustek

Ваше предложение «... обработать запрос, отправить ответ и остановить ()» не будет работать для Chrome, потому что к тому времени библиотека уже проигнорировала пакет SYN, устанавливающий второе соединение. Кроме того, Chrome ожидает, что оба соединения будут открыты одновременно. Это абсолютно законное использование протокола TCP/IP., @Len Shustek

но прослушивающий сокет TCP находится на ESP32. он должен обрабатывать TCP-часть установления TCP-соединения. и тогда соединение должно ждать пока до него доберется скетч, @Juraj

Согласен, должно. Но это не так. Обратите внимание, что он никогда не отвечает на пакет 180, который пытается установить соединение через порт 62347. Могу ли я что-то сделать не так, что не позволяет?, @Len Shustek