Dziś druga część opisu zmagań o przywrócenie połączenia między aplikacją a modułem w komunikacji Websocket.
Jakoś nie mogłem uwierzyć, że tak dopracowana biblioteka może zawierać błąd zawieszający (co prawda sporadycznie) komunikację pomiędzy klientem a serwerem. Podejrzewałem raczej, że to niedostatek mojej wiedzy nie pozwolił mi odpowiednio skonfigurować biblioteki WebSockets by działała bezbłędnie.
I oczywiście nie pomyliłem się.
Jak już wspomniałem kontrolę obecności odbiorcy po drugiej stronie kanału dokonuje procedura zwana Heartbeat . Transmituje i odbiera ona ramki ping-pong służące do wykrycia ewentualnych zakłóceń w przesyle komunikatów i ustawienia ich odbiorcy w status ROZŁĄCZONY.
Ramki ping-pong na pewno generuje aplikacja Virtuino. Ponadto w przypadku rozłączenia się z serwerem próbuje ona cyklicznie (co około 5 sekund) wznowić połączenie. I to powinno wystarczyć by zerwane sztywne łącze między klientem a serwerem powróciło.
Niestety. Serwer w pierwotnej konfiguracji dostępnej na stronach VIRTUINO ( np. ESP8266 webSocket Server example ) nic nie wie o uracie połączenia z klientem. Status tego wciąż pozostaje jako POŁĄCZONY mimo zerwanej komunikacji. Co więcej, pozostając w stanie połączenia nie odbiera kolejnych poleceń typu Reconnect od klienta. A w zasadzie odbiera ale otwiera nowe kanały komunikacji z kolejnymi numerami klientów ( [1] [2] [3] i [4]). Kolejni klienci mimo że posiadają ten sam numer IP nie są traktowani jako ten sam pierwotny klient z numerem [0]. I żadne komunikaty z aplikacji nie docierają we właściwe miejsce w serwerze. Stąd i brak komunikacji między stronami.
By ją przywrócić należy zresetować serwer (lub tylko bibliotekę WebSockets). I komunikacja zostaje ponownie nawiązana gdyż serwer staruje z pozycji, iż wszyscy klienci są rozłączeni,
Byłem ciekaw dlaczego komunikacja zostaje przywrócona również gdy restartuje się aplikacja. I wyszło mi na to, że przed wyłączeniem Virtuino musi wysyłać do serwera komendę ROZŁĄCZ, która powoduje w serwerze rzeczywiste ustawienie statusu klienta jako ROZŁĄCZONY. Ale to musiałby potwierdzić Ilias :)
Wszystko to można zobaczyć dodając do funkcji z przykładu dwa ostatnie case
void webSocketEvent(uint8_t num, WStype_t type, uint8_t* payload, size_t length) {
switch (type) {
case WStype_DISCONNECTED:
Serial.printf("[%u] Disconnected!\n", num);
break;
case WStype_CONNECTED:
{
IPAddress ip = webSocket.remoteIP(num);
Serial.printf("[%u] New client connected - IP: %d.%d.%d.%d \n", num, ip[0], ip[1], ip[2], ip[3]);
sendPinsStatus(); // send the initial pin and variable values to the connected clients
break;
}
case WStype_TEXT: // a new message received
{
Serial.printf("[%u] Received: %s\n", num, payload);
//-- The incoming payload is a json message. The following code extracts the value from json without extra library
String str = (char*)payload;
int p1 = str.indexOf("{\"");
if (p1 == 0) {
int p2 = str.indexOf("\":");
if (p2 > p1) {
String tag = str.substring(p1 + 2, p2);
p1 = str.indexOf(":\"", p2);
if (p1 > 0) {
p2 = str.lastIndexOf("\"}");
if (p2 > 0) {
String value = str.substring(p1 + 2, p2);
onValueReceived(tag, value);
}
}
}
}
break;
}
case WStype_PING: // pong will be send automatically
{
Serial.printf("get ping*****************");
break;
}
case WStype_PONG: // answer to a ping we send
{
Serial.printf("get pong*---------------*");
break;
}
}
}
Można nimi wyświetlić komunikat gdy ramka ping lub pong zostanie odebrana. W pierwotnym przykładowym kodzie można zobaczyć jak od klienta nadchodzą ramki ping
Nie natomiast ramek pong. Poprzedni mój wpis służył temu by zrobić dodatkowy mechanizm ping-pong od strony serwera tak by serwer mógł testować dostępność klienta po drugiej stronie kanału.
A wystarczyło dodać jedną linijkę kodu w sekcji setup(), która uruchomi procedurę Heartbeat
webSocket.enableHeartbeat(5000, 3000, 2);
i już możemy zobaczyć jak serwer odbiera ramki i ping i pong.
Brak komentarzy:
Prześlij komentarz