8000 Merge pull request #383 from sovcik/master · FEmbed/arduinoWebSockets@72731be · GitHub
[go: up one dir, main page]

Skip to content

Commit 72731be

Browse files
authored
Merge pull request Links2004#383 from sovcik/master
Implementing automatic WS client-side heartbeat
2 parents eaef4f0 + 1b6b42b commit 72731be

File tree

7 files changed

+116
-4
lines changed

7 files changed

+116
-4
lines changed

examples/esp8266/WebSocketClient/WebSocketClient.ino

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,12 @@ void setup() {
8484

8585
// try ever 5000 again if connection has failed
8686
webSocket.setReconnectInterval(5000);
87+
88+
// start heartbeat (optional)
89+
// ping server every 15000 ms
90+
// expect pong from server within 3000 ms
91+
// consider connection disconnected if pong is not received 2 times
92+
webSocket.enableHeartbeat(15000, 3000, 2);
8793

8894
}
8995

library.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@
1313
"type": "git",
1414
"url": "https://github.com/Links2004/arduinoWebSockets.git"
1515
},
16-
"version": "2.1.2",
16+
"version": "2.1.3",
1717
"license": "LGPL-2.1",
1818
"export": {
1919
"exclude": [

library.properties

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
name=WebSockets
2-
version=2.1.2
2+
version=2.1.3
33
author=Markus Sattler
44
maintainer=Markus Sattler
55
sentence=WebSockets for Arduino (Server + Client)

src/WebSockets.cpp

Lines changed: 45 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,46 @@ 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+
clientDisconnect(client);
693+
}
694+
}
695+
}
696+
697+
}
698+
699+
}

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: 48 additions & 2 deletions
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
@@ -170,6 +174,12 @@ void WebSocketsClient::loop(void) {
170174
}
171175
} else {
172176
handleClientData();
177+
178+
if (_client.status == WSC_CONNECTED){
179+
handleHBPing();
180+
handleHBTimeout(&_client);
181+
}
182+
173183
}
174184
}
175185
#endif
@@ -243,7 +253,10 @@ bool WebSocketsClient::sendBIN(const uint8_t * payload, size_t length) {
243253
*/
244254
bool WebSocketsClient::sendPing(uint8_t * payload, size_t length) {
245255
if(clientIsConnected(&_client)) {
246-
return sendFrame(&_client, WSop_ping, payload, length);
256+
bool sent = sendFrame(&_client, WSop_ping, payload, length);
257+
if (sent)
258+
_client.lastPing = millis();
259+
return sent;
247260
}
248261
return false;
249262
}
@@ -715,7 +728,7 @@ void WebSocketsClient::connectedCb() {
715728
}
716729

717730
void WebSocketsClient::connectFailedCb() {
718-
DEBUG_WEBSOCKETS("[WS-Client] connection to %s:%u Faild\n", _host.c_str(), _port);
731+
DEBUG_WEBSOCKETS("[WS-Client] connection to %s:%u Failed\n", _host.c_str(), _port);
719732
}
720733

721734
#if (WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP8266_ASYNC)
@@ -761,3 +774,36 @@ void WebSocketsClient::asyncConnect() {
761774
}
762775

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

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)
0