Links2004 / arduinoWebSockets

arduinoWebSockets
GNU Lesser General Public License v2.1
1.89k stars 555 forks source link

WebSocket Server will not accept new clients after running for a long while (ESP32) #314

Open JacoFourie opened 6 years ago

JacoFourie commented 6 years ago

Hi all.

I have been using this library for a while now and all works 100%. I did pick up something new and that is if the ESP32 has been running for a long time like more than 5 days the websocket server will not accept any new connections. Those connections that are still there will work. But as soon as you try to refresh a browser or try a new one the client will not connect. Why would that be and how can I reset the server? If there is a way to reset the server let say once a day then I will do that to see if it fixes the issue (Should I just issue a begin and set the callback again on a daily basis ?). The network is fine as I send data via MQTT as well and that still works fine.

Here is the ESP32 using this lib in action

https://www.youtube.com/watch?v=jYnhPLxoM1k&t=6s

Links2004 commented 6 years ago

Hi,

Nice project. do you send data from time to time like a ping from the ESP? the lwIP TCP stack on the ESP is only able to detect a connection loss when you try to send data. may the sendPing / broadcastPing function will help you (https://github.com/Links2004/arduinoWebSockets/blob/master/src/WebSocketsServer.h#L85-L89)

how many clients do you connect in parallel? the current limit it set 5 (https://github.com/Links2004/arduinoWebSockets/blob/master/src/WebSocketsServer.h#L31) this is from ESP8266 times since the lwIP stack has a very low max parralel socket / connection limit. for the ESP32 this may can be increased.

if all it not helping you can force a disconnect of all or one client via disconnect https://github.com/Links2004/arduinoWebSockets/blob/master/src/WebSocketsServer.h#L91-L92

JacoFourie commented 6 years ago

Thanks. I want to replace the big unit usin only the ESP32. I am almost there.

Here you can see the big unit with the Raspberry Pi and Arduino in action

https://www.youtube.com/watch?v=yAx9-Uk6Lck&t=14s

I have a timer that sends the data every 2 seconds. Before I send I will do a ping and disconnect all connections that I am not able to ping. I found that if a client connects via a VPN and the VPN drops without closing the browser then the server will think the client is still there. And then it will block the send making the ESP wait a very long time before it times out. And because all my wifi stuff sits on the same CPU as the websocket send then nothing will work on the wifi side.

I don't need a lot of connections. I have tested it up to 20 and all works 100%. The issues seems to be that the server will not fire the new connection event after it has been running for a long time. The data still flows to those browsers connected but will not accept any new ones. I also have code that tries to reconnect from the client side if it looses its connection. Now that I think about it maybe that code is DDOS ing the server but I don't think so. Will have to check. All it does is to reconnect from the browser side should it loose connection. So if the ESP32 reboots the browser will reconnect on its own.

Here is a short snippet of the send.

Something I am not sure about is how to disconnect the clients. Let say I have 5 connections and I do not get a response from 3 and I disconnect it. Will the server reuse 3 next time a client tries to reconnect or will it use 6 ? If so then what will happen if I try to ping 3. Will it try to disconnect 3 every loop then ?

if (send_timer.TimeHasChanged() ) // this prevents the time from being constantly shown.
      {         
        if(send_timer.ShowSeconds() >= 2) {

              digitalWrite(2, HIGH);

              Serial.print("Temp :");
              Serial.println(temp_c , 2);

              Serial.println("Get Actuator info");
              get_actuator_info();

              dtostrf(temp_c, 0, 2, desp_temp);
              // Prepare a JSON payload string
              String payload = "{";
              payload += "\"temperature\":"; payload += desp_temp; payload += ",";
              payload += "\"target\":"; payload += target_temp; payload += ",";
              payload += "\"wifi\":"; payload += wifi_quality; payload += ",";
              payload += "\"actuator\":"; payload += actuator_feedback; payload += ",";
              payload += "\"vsd_speed\":"; payload += vsd_run_speed; payload += ",";
              payload += "\"vsd_temp\":"; payload += vsd_temp; payload += ",";
              payload += "\"vsd_status\":"; payload += vsd_status; payload += ",";
              payload += "\"vsd_error\":"; payload += vsd_error_code; payload += ",";
              payload += "\"vsd_kw\":"; payload += vsd_kw;  payload += ",";
              payload += "\"power\":"; payload += power_status; payload += ",";
              payload += "\"battery\":"; payload += battery_status; payload += ",";              
              payload += "\"sensor\":"; payload += sensor_status; payload += ",";
              payload += "\"stoker\":"; payload += stoker_status; payload += ",";
              payload += "\"stoker_overload\":"; payload += stoker_overload_status;               
              payload += "}";

              char send_char[256];
              payload.toCharArray( send_char, 256 );  

              Serial.println("Sending ping to all clients");               
              for(uint8_t i = 0; i < WEBSOCKETS_SERVER_CLIENT_MAX; i++) {
                 if(!webSocket.sendPing(i)){
                    //If we do not get a pong disconnect the client
                    webSocket.disconnect(i);                  
                 }                
              }              
              Serial.println("Sending info via websockets");   
              webSocket.broadcastTXT(payload);

              Serial.println("Sending info via MQTT");             
              mqtt_client.publish( "TempLogger/AllValues", send_char );                       

              send_timer.ResetTimer();
              Serial.println("Done");
              digitalWrite(2, LOW);             

      }
    }
JoarGjersund commented 4 years ago

this might be the same thing as #526. It looks like it is the websocket.broadcast or websocket.loop that is causing the trouble. found a workaround by closing all connections if those functions takes to long time to return . (on the client side, I made it so they will automatically try to reconnect when connection is closed). The workaround is however not very good for real time applications, since there is no definite timeout on those two functions. Optimally it should be possible to set the timeout of each function that is running in the main program loop

 int t0 = millis();
  webSocket.broadcastPing(msg);
  Serial.println("ping.");

  webSocket.loop();
  Serial.println("websocket loop.");
  if (millis()-t0 > 100){
    webSocket.disconnect();
  }
Links2004 commented 4 years ago

can you check with the latest master, the yield handling has been reworked which may helps. since the TCP stack gets more time to handle packets and disconnected.

may relates to:

526