Skip to content

Commit 68800e2

Browse files
committed
implementing heartbeat
1 parent eaef4f0 commit 68800e2

File tree

4 files changed

+106
-1
lines changed

4 files changed

+106
-1
lines changed

src/WebSockets.cpp

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -433,10 +433,12 @@ void WebSockets::handleWebsocketPayloadCb(WSclient_t * client, bool ok, uint8_t
433433
break;
434434
case WSop_ping:
435435
// send pong back
436+
DEBUG_WEBSOCKETS("[WS][%d][handleWebsocket] ping received (%s)\n", client->num, payload ? (const char*)payload : "");
436437
sendFrame(client, WSop_pong, payload, header->payloadLen);
437438
break;
438439
case WSop_pong:
439440
DEBUG_WEBSOCKETS("[WS][%d][handleWebsocket] get pong (%s)\n", client->num, payload ? (const char*)payload : "");
441+
client->pongReceived = true;
440442
break;
441443
case WSop_close: {
442444
#ifndef NODEBUG_WEBSOCKETS
@@ -652,3 +654,47 @@ size_t WebSockets::write(WSclient_t * client, const char *out) {
652654
if(out == NULL) return 0;
653655
return write(client, (uint8_t*)out, strlen(out));
654656
}
657+
658+
/**
659+
* enable ping/pong heartbeat process
660+
* @param client WSclient_t *
661+
* @param pingInterval uint32_t how often ping will be sent
662+
* @param pongTimeout uint32_t millis after which pong should timout if not received
663+
* @param disconnectTimeoutCount uint8_t how many timeouts before disconnect, 0=> do not disconnect
664+
*/
665+
void WebSockets::enableHeartbeat(WSclient_t * client, uint32_t pingInterval, uint32_t pongTimeout, uint8_t disconnectTimeoutCount){
666+
if(client == NULL) return;
667+
client->pingInterval = pingInterval;
668+
client->pongTimeout = pongTimeout;
669+
client->disconnectTimeoutCount = disconnectTimeoutCount;
670+
671+
}
672+
673+
/**
674+
* handle ping/pong heartbeat timeout process
675+
* @param client WSclient_t *
676+
*/
677+
void WebSockets::handleHBTimeout(WSclient_t * client){
678+
if (client->pingInterval) { // if heartbeat is enabled
679+
uint32_t pi = millis() - client->lastPing;
680+
681+
if (client->pongReceived) {
682+
client->pongTimeoutCount = 0;
683+
} else {
684+
if (pi > client->pongTimeout){ // pong not received in time
685+
client->pongTimeoutCount++;
686+
client->lastPing = millis() - client->pingInterval - 500; // force ping on the next run
687+
688+
DEBUG_WEBSOCKETS("[HBtimeout] pong TIMEOUT! lp=%d millis=%d pi=%d count=%d\n", client->lastPing, millis(), pi, client->pongTimeoutCount);
689+
690+
if (client->disconnectTimeoutCount && client->pongTimeoutCount >= client->disconnectTimeoutCount){
691+
DEBUG_WEBSOCKETS("[HBtimeout] count=%d, DISCONNECTING\n", client->pongTimeoutCount);
692+
client->status = WSC_NOT_CONNECTED;
693+
//clientDisconnect(client);
694+
}
695+
}
696+
}
697+
698+
}
699+
700+
}

src/WebSockets.h

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -264,6 +264,13 @@ typedef struct {
264264
bool cHttpHeadersValid; ///< non-websocket http header validity indicator
265265
size_t cMandatoryHeadersCount; ///< non-websocket mandatory http headers present count
266266

267+
bool pongReceived;
268+
uint32_t pingInterval; // how often ping will be sent, 0 means "heartbeat is not active"
269+
uint32_t lastPing; // millis when last pong has been received
270+
uint32_t pongTimeout; // interval in millis after which pong is considered to timeout
271+
uint8_t disconnectTimeoutCount; // after how many subsequent pong timeouts discconnect will happen, 0 means "do not disconnect"
272+
uint8_t pongTimeoutCount; // current pong timeout count
273+
267274
#if (WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP8266_ASYNC)
268275
String cHttpLine; ///< HTTP header lines
269276
#endif
@@ -303,6 +310,9 @@ class WebSockets {
303310
virtual size_t write(WSclient_t * client, uint8_t *out, size_t n);
304311
size_t write(WSclient_t * client, const char *out);
305312

313+
void enableHeartbeat(WSclient_t * client, uint32_t pingInterval, uint32_t pongTimeout, uint8_t disconnectTimeoutCount);
314+
void handleHBTimeout(WSclient_t * client);
315+
306316

307317
};
308318

src/WebSocketsClient.cpp

Lines changed: 45 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,10 @@ void WebSocketsClient::begin(const char *host, uint16_t port, const char * url,
6666
_client.plainAuthorization = "";
6767
_client.isSocketIO = false;
6868

69+
_client.lastPing = 0;
70+
_client.pongReceived = false;
71+
_client.pongTimeoutCount = 0;
72+
6973
#ifdef ESP8266
7074
randomSeed(RANDOM_REG32);
7175
#else
@@ -169,6 +173,10 @@ void WebSocketsClient::loop(void) {
169173

170174
}
171175
} else {
176+
if (_client.status == WSC_CONNECTED){
177+
handleHBPing();
178+
handleHBTimeout(&_client);
179+
}
172180
handleClientData();
173181
}
174182
}
@@ -243,7 +251,10 @@ bool WebSocketsClient::sendBIN(const uint8_t * payload, size_t length) {
243251
*/
244252
bool WebSocketsClient::sendPing(uint8_t * payload, size_t length) {
245253
if(clientIsConnected(&_client)) {
246-
return sendFrame(&_client, WSop_ping, payload, length);
254+
bool sent = sendFrame(&_client, WSop_ping, payload, length);
255+
if (sent)
256+
_client.lastPing = millis();
257+
return sent;
247258
}
248259
return false;
249260
}
@@ -761,3 +772,36 @@ void WebSocketsClient::asyncConnect() {
761772
}
762773

763774
#endif
775+
776+
/**
777+
* send heartbeat ping to server in set intervals
778+
*/
779+
void WebSocketsClient::handleHBPing(){
780+
if (_client.pingInterval == 0) return;
781+
uint32_t pi = millis() - _client.lastPing;
782+
if (pi > _client.pingInterval){
783+
DEBUG_WEBSOCKETS("[WS-Client] sending HB ping\n");
784+
if (sendPing()) {
785+
_client.lastPing = millis();
786+
_client.pongReceived = false;
787+
}
788+
}
789+
790+
}
791+
792+
/**
793+
* enable ping/pong heartbeat process
794+
* @param pingInterval uint32_t how often ping will be sent
795+
* @param pongTimeout uint32_t millis after which pong should timout if not received
796+
* @param disconnectTimeoutCount uint8_t how many timeouts before disconnect, 0=> do not disconnect
797+
*/
798+
void WebSocketsClient::enableHeartbeat(uint32_t pingInterval, uint32_t pongTimeout, uint8_t disconnectTimeoutCount){
799+
WebSockets::enableHeartbeat(&_client, pingInterval, pongTimeout, disconnectTimeoutCount);
800+
}
801+
802+
/**
803+
* disable ping/pong heartbeat process
804+
*/
805+
void WebSocketsClient::disableHeartbeat(){
806+
_client.pingInterval = 0;
807+
}

src/WebSocketsClient.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -86,6 +86,9 @@ class WebSocketsClient: private WebSockets {
8686

8787
void setReconnectInterval(unsigned long time);
8888

89+
void enableHeartbeat(uint32_t pingInterval, uint32_t pongTimeout, uint8_t disconnectTimeoutCount);
90+
void disableHeartbeat();
91+
8992
protected:
9093
String _host;
9194
uint16_t _port;
@@ -115,6 +118,8 @@ class WebSocketsClient: private WebSockets {
115118
void connectedCb();
116119
void connectFailedCb();
117120

121+
void handleHBPing(); // send ping in specified intervals
122+
118123
#if (WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP8266_ASYNC)
119124
void asyncConnect();
120125
#endif

0 commit comments

Comments
 (0)