Несколько подключений 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.
@Len Shustek, 👍1
1 ответ
Сервер.доступный() в сетевых библиотеках Arduino всегда возвращает первого подключенного клиента. Это противоречит библиотекам WiFi ESP8266 и ESP32 для Arduino. Они помечают возвращенное соединение как заявленное и больше не возвращают его. Если WiFiClient на ESP выходит за рамки, он закрывается. В библиотеках Arduino вы должны остановить () клиент, чтобы закрыть соединение и освободить «сокет».
В библиотеке Arduino Ethernet есть новая функция, еще не реализованная в библиотеках WiFi. Это server.accept(). Он помечает соединение как принятое, и следующий вызов не возвращает того же клиента. Скетч по-прежнему отвечает за вызов stop(). Использование accept() продемонстрировано в примере AdvancedChatServer библиотеки Ethernet 2.00.
Что вы можете сделать? Получите соединение (WiFiClient), обработайте запрос, отправьте ответ и остановите () соединение. Затем обработайте следующего клиента. Установите заголовок Expires, чтобы позволить браузеру кэшировать статические файлы.
- TCP-клиент readStringUntil только при наличии данных
- Веб-сервер ESP8266 недоступен через 2 минуты после сброса
- ESP32 не предоставляет IP-адрес шлюза в точке доступа
- Несколько клиентских серверов через Wi-Fi
- WebSocketsServer.h: No such file or directory
- контент» не захватывается
- Как разрешить междоменные запросы на ESP8266 WebServer
- ESP8266 TCP-соединение WiFiClient проблема
«Сервер.доступный() в сетевых библиотеках Arduino всегда возвращает первого подключенного клиента». Похоже, это не относится к WiFiMINA. Это правда, когда я впервые вызываю его в своем примере после подключения («Клиент 2»), но затем, как только данные доступны для подключения, server.available() возвращает ноль., @Len Shustek
Ваше предложение «... обработать запрос, отправить ответ и остановить ()» не будет работать для Chrome, потому что к тому времени библиотека уже проигнорировала пакет SYN, устанавливающий второе соединение. Кроме того, Chrome ожидает, что оба соединения будут открыты одновременно. Это абсолютно законное использование протокола TCP/IP., @Len Shustek
но прослушивающий сокет TCP находится на ESP32. он должен обрабатывать TCP-часть установления TCP-соединения. и тогда соединение должно ждать пока до него доберется скетч, @Juraj
Согласен, должно. Но это не так. Обратите внимание, что он никогда не отвечает на пакет 180, который пытается установить соединение через порт 62347. Могу ли я что-то сделать не так, что не позволяет?, @Len Shustek