From d2043bf8ef35257efc8f1477bc9fe57c334f0c85 Mon Sep 17 00:00:00 2001 From: Links Date: Sat, 12 May 2018 15:13:50 +0200 Subject: [PATCH 01/11] start work for socket.IO basic Engine.IO implementation --- src/SocketIOclient.cpp | 88 ++++++++++++++++++++++++++++++++++++++++ src/SocketIOclient.h | 58 ++++++++++++++++++++++++++ src/WebSocketsClient.cpp | 4 ++ src/WebSocketsClient.h | 1 + 4 files changed, 151 insertions(+) create mode 100644 src/SocketIOclient.cpp create mode 100644 src/SocketIOclient.h diff --git a/src/SocketIOclient.cpp b/src/SocketIOclient.cpp new file mode 100644 index 0000000..b2e35e3 --- /dev/null +++ b/src/SocketIOclient.cpp @@ -0,0 +1,88 @@ +/* + * SocketIOclient.cpp + * + * Created on: May 12, 2018 + * Author: links + */ + +#include "WebSockets.h" +#include "WebSocketsClient.h" +#include "SocketIOclient.h" + +SocketIOclient::SocketIOclient() { + +} + +SocketIOclient::~SocketIOclient() { + +} + +void SocketIOclient::begin(const char *host, uint16_t port, const char * url, const char * protocol) { + WebSocketsClient::beginSocketIO(host, port, url, protocol); +} + +void SocketIOclient::begin(String host, uint16_t port, String url, String protocol) { + WebSocketsClient::beginSocketIO(host, port, url, protocol); +} + +void SocketIOclient::loop(void) { + WebSocketsClient::loop(); + unsigned long t = millis(); + if((t - _lastConnectionFail) > EIO_HEARTBEAT_INTERVAL) { + _lastConnectionFail = t; + sendTXT(eIOtype_PING); + } +} + +void SocketIOclient::runCbEvent(WStype_t type, uint8_t * payload, size_t length) { + switch(type) { + case WStype_DISCONNECTED: + DEBUG_WEBSOCKETS("[wsIOc] Disconnected!\n"); + break; + case WStype_CONNECTED: { + DEBUG_WEBSOCKETS("[wsIOc] Connected to url: %s\n", payload); + // send message to server when Connected + // socket.io upgrade confirmation message (required) + sendTXT(eIOtype_UPGRADE); + } + break; + case WStype_TEXT: { + + if(length < 1) { + break; + } + + engineIOmessageType_t eType = (engineIOmessageType_t) payload[0]; + switch(eType) { + case eIOtype_PING: + payload[0] = eIOtype_PONG; + DEBUG_WEBSOCKETS("[wsIOc] get ping send pong (%s)\n", payload); + sendTXT(payload, length); + break; + case eIOtype_PONG: + DEBUG_WEBSOCKETS("[wsIOc] get pong\n"); + break; + case eIOtype_OPEN: + case eIOtype_CLOSE: + case eIOtype_MESSAGE: + case eIOtype_UPGRADE: + case eIOtype_NOOP: + default: + DEBUG_WEBSOCKETS("[wsIOc] Engine.IO Message Type %c (%02X) is not implemented\n", eType, eType); + DEBUG_WEBSOCKETS("[wsIOc] get text: %s\n", payload); + break; + } + + // send message to server + // webSocket.sendTXT("message here"); + } + break; + case WStype_BIN: + DEBUG_WEBSOCKETS("[wsIOc] get binary length: %u\n", length); + // hexdump(payload, length); + + // send data to server + // webSocket.sendBIN(payload, length); + break; + } +} diff --git a/src/SocketIOclient.h b/src/SocketIOclient.h new file mode 100644 index 0000000..df53278 --- /dev/null +++ b/src/SocketIOclient.h @@ -0,0 +1,58 @@ +/* + * SocketIOclient.h + * + * Created on: May 12, 2018 + * Author: links + */ + +#ifndef SOCKETIOCLIENT_H_ +#define SOCKETIOCLIENT_H_ + +#include "WebSockets.h" + +#define EIO_HEARTBEAT_INTERVAL 10000 + +typedef enum { + eIOtype_OPEN = '0', ///< Sent from the server when a new transport is opened (recheck) + eIOtype_CLOSE = '1', ///< Request the close of this transport but does not shutdown the connection itself. + eIOtype_PING = '2', ///< Sent by the client. Server should answer with a pong packet containing the same data + eIOtype_PONG = '3', ///< Sent by the server to respond to ping packets. + eIOtype_MESSAGE = '4', ///< actual message, client and server should call their callbacks with the data + eIOtype_UPGRADE = '5', ///< Before engine.io switches a transport, it tests, if server and client can communicate over this transport. If this test succeed, the client sends an upgrade packets which requests the server to flush its cache on the old transport and switch to the new transport. + eIOtype_NOOP = '6', ///< A noop packet. Used primarily to force a poll cycle when an incoming websocket connection is received. +} engineIOmessageType_t; + + +typedef enum { + sIOtype_CONNECT = '0', + sIOtype_DISCONNECT = '1', + sIOtype_EVENT = '2', + sIOtype_ACK = '3', + sIOtype_ERROR = '4', + sIOtype_BINARY_EVENT = '5', + sIOtype_BINARY_ACK = '6', +} socketIOmessageType_t; + +class SocketIOclient: private WebSocketsClient { + + public: +#ifdef __AVR__ + typedef void (*SocketIOclientEvent)(WStype_t type, uint8_t * payload, size_t length); +#else + typedef std::function SocketIOclientEvent; +#endif + + SocketIOclient(void); + virtual ~SocketIOclient(void); + + void begin(const char *host, uint16_t port, const char * url = "/socket.io/?EIO=3", const char * protocol = "arduino"); + void begin(String host, uint16_t port, String url = "/socket.io/?EIO=3", String protocol = "arduino"); + + + void loop(void); + private: + void runCbEvent(WStype_t type, uint8_t * payload, size_t length); + uint64_t _lastHeartbeat = 0; +}; + +#endif /* SOCKETIOCLIENT_H_ */ diff --git a/src/WebSocketsClient.cpp b/src/WebSocketsClient.cpp index b211df2..79184db 100644 --- a/src/WebSocketsClient.cpp +++ b/src/WebSocketsClient.cpp @@ -215,6 +215,10 @@ bool WebSocketsClient::sendTXT(String & payload) { return sendTXT((uint8_t *) payload.c_str(), payload.length()); } +bool WebSocketsClient::sendTXT(char payload) { + return sendTXT((uint8_t *) &payload, 1); +} + /** * send binary data to client * @param num uint8_t client id diff --git a/src/WebSocketsClient.h b/src/WebSocketsClient.h index 8e6305e..db02300 100644 --- a/src/WebSocketsClient.h +++ b/src/WebSocketsClient.h @@ -70,6 +70,7 @@ class WebSocketsClient: private WebSockets { bool sendTXT(char * payload, size_t length = 0, bool headerToPayload = false); bool sendTXT(const char * payload, size_t length = 0); bool sendTXT(String & payload); + bool sendTXT(char payload); bool sendBIN(uint8_t * payload, size_t length, bool headerToPayload = false); bool sendBIN(const uint8_t * payload, size_t length); From 8967356af3fb925b43b6f2405a78045942e01bca Mon Sep 17 00:00:00 2001 From: Links Date: Sat, 12 May 2018 15:27:08 +0200 Subject: [PATCH 02/11] improve ram usage on one char send --- src/SocketIOclient.cpp | 2 +- src/WebSocketsClient.cpp | 4 +++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/src/SocketIOclient.cpp b/src/SocketIOclient.cpp index b2e35e3..619cf7d 100644 --- a/src/SocketIOclient.cpp +++ b/src/SocketIOclient.cpp @@ -42,7 +42,7 @@ void SocketIOclient::runCbEvent(WStype_t type, uint8_t * payload, size_t length) case WStype_CONNECTED: { DEBUG_WEBSOCKETS("[wsIOc] Connected to url: %s\n", payload); // send message to server when Connected - // socket.io upgrade confirmation message (required) + // Engine.io upgrade confirmation message (required) sendTXT(eIOtype_UPGRADE); } break; diff --git a/src/WebSocketsClient.cpp b/src/WebSocketsClient.cpp index 79184db..d5e6f47 100644 --- a/src/WebSocketsClient.cpp +++ b/src/WebSocketsClient.cpp @@ -216,7 +216,9 @@ bool WebSocketsClient::sendTXT(String & payload) { } bool WebSocketsClient::sendTXT(char payload) { - return sendTXT((uint8_t *) &payload, 1); + uint8_t buf[WEBSOCKETS_MAX_HEADER_SIZE + 2] = {0x00}; + buf[WEBSOCKETS_MAX_HEADER_SIZE] = payload; + return sendTXT(buf, 1, true); } /** From f95c0143424995d4b16e7d3ea6e2b07554eae890 Mon Sep 17 00:00:00 2001 From: Links Date: Sat, 12 May 2018 16:53:34 +0200 Subject: [PATCH 03/11] basic event sending works --- src/SocketIOclient.cpp | 62 +++++++++++++- src/SocketIOclient.h | 15 +++- src/WebSockets.cpp | 169 +++++++++++++++++++++++++-------------- src/WebSockets.h | 3 + src/WebSocketsClient.cpp | 11 +++ src/WebSocketsClient.h | 4 +- 6 files changed, 199 insertions(+), 65 deletions(-) diff --git a/src/SocketIOclient.cpp b/src/SocketIOclient.cpp index 619cf7d..d2b2f28 100644 --- a/src/SocketIOclient.cpp +++ b/src/SocketIOclient.cpp @@ -25,12 +25,68 @@ void SocketIOclient::begin(String host, uint16_t port, String url, String protoc WebSocketsClient::beginSocketIO(host, port, url, protocol); } +bool SocketIOclient::isConnected(void) { + return WebSocketsClient::isConnected(); +} + +/** + * send text data to client + * @param num uint8_t client id + * @param payload uint8_t * + * @param length size_t + * @param headerToPayload bool (see sendFrame for more details) + * @return true if ok + */ +bool SocketIOclient::sendEVENT(uint8_t * payload, size_t length, bool headerToPayload) { + bool ret = false; + if(length == 0) { + length = strlen((const char *) payload); + } + if(clientIsConnected(&_client)) { + + if(!headerToPayload) { + // webSocket Header + ret = WebSocketsClient::sendFrameHeader(&_client, WSop_text, length + 2, true, true); + // Engine.IO / Socket.IO Header + if(ret) { + uint8_t buf[3] = { eIOtype_MESSAGE, sIOtype_EVENT, 0x00 }; + ret = WebSocketsClient::write(&_client, buf, 2); + } + if(ret) { + ret = WebSocketsClient::write(&_client, payload, length ); + } + return ret; + } else { + // TODO implement + } + + // return WebSocketsClient::sendFrame(&_client, WSop_text, payload, length, true, true, headerToPayload); + } + return false; +} + +bool SocketIOclient::sendEVENT(const uint8_t * payload, size_t length) { + return sendEVENT((uint8_t *) payload, length); +} + +bool SocketIOclient::sendEVENT(char * payload, size_t length, bool headerToPayload) { + return sendEVENT((uint8_t *) payload, length, headerToPayload); +} + +bool SocketIOclient::sendEVENT(const char * payload, size_t length) { + return sendEVENT((uint8_t *) payload, length); +} + +bool SocketIOclient::sendEVENT(String & payload) { + return sendEVENT((uint8_t *) payload.c_str(), payload.length()); +} + void SocketIOclient::loop(void) { WebSocketsClient::loop(); unsigned long t = millis(); if((t - _lastConnectionFail) > EIO_HEARTBEAT_INTERVAL) { _lastConnectionFail = t; - sendTXT(eIOtype_PING); + //WebSocketsClient::sendTXT(eIOtype_PING); } } @@ -43,7 +99,7 @@ void SocketIOclient::runCbEvent(WStype_t type, uint8_t * payload, size_t length) DEBUG_WEBSOCKETS("[wsIOc] Connected to url: %s\n", payload); // send message to server when Connected // Engine.io upgrade confirmation message (required) - sendTXT(eIOtype_UPGRADE); + WebSocketsClient::sendTXT(eIOtype_UPGRADE); } break; case WStype_TEXT: { @@ -57,7 +113,7 @@ void SocketIOclient::runCbEvent(WStype_t type, uint8_t * payload, size_t length) case eIOtype_PING: payload[0] = eIOtype_PONG; DEBUG_WEBSOCKETS("[wsIOc] get ping send pong (%s)\n", payload); - sendTXT(payload, length); + WebSocketsClient::sendTXT(payload, length, false); break; case eIOtype_PONG: DEBUG_WEBSOCKETS("[wsIOc] get pong\n"); diff --git a/src/SocketIOclient.h b/src/SocketIOclient.h index df53278..b179954 100644 --- a/src/SocketIOclient.h +++ b/src/SocketIOclient.h @@ -12,6 +12,9 @@ #define EIO_HEARTBEAT_INTERVAL 10000 +#define EIO_MAX_HEADER_SIZE (WEBSOCKETS_MAX_HEADER_SIZE + 1) +#define SIO_MAX_HEADER_SIZE (EIO_MAX_HEADER_SIZE + 1) + typedef enum { eIOtype_OPEN = '0', ///< Sent from the server when a new transport is opened (recheck) eIOtype_CLOSE = '1', ///< Request the close of this transport but does not shutdown the connection itself. @@ -33,7 +36,7 @@ typedef enum { sIOtype_BINARY_ACK = '6', } socketIOmessageType_t; -class SocketIOclient: private WebSocketsClient { +class SocketIOclient: protected WebSocketsClient { public: #ifdef __AVR__ @@ -48,9 +51,17 @@ class SocketIOclient: private WebSocketsClient { void begin(const char *host, uint16_t port, const char * url = "/socket.io/?EIO=3", const char * protocol = "arduino"); void begin(String host, uint16_t port, String url = "/socket.io/?EIO=3", String protocol = "arduino"); + bool isConnected(void); + + bool sendEVENT(uint8_t * payload, size_t length = 0, bool headerToPayload = false); + bool sendEVENT(const uint8_t * payload, size_t length = 0); + bool sendEVENT(char * payload, size_t length = 0, bool headerToPayload = false); + bool sendEVENT(const char * payload, size_t length = 0); + bool sendEVENT(String & payload); void loop(void); - private: + + protected: void runCbEvent(WStype_t type, uint8_t * payload, size_t length); uint64_t _lastHeartbeat = 0; }; diff --git a/src/WebSockets.cpp b/src/WebSockets.cpp index 86ba98f..95cd902 100644 --- a/src/WebSockets.cpp +++ b/src/WebSockets.cpp @@ -71,6 +71,115 @@ void WebSockets::clientDisconnect(WSclient_t * client, uint16_t code, char * rea clientDisconnect(client); } +/** + * + * @param buf uint8_t * ptr to the buffer for writing + * @param opcode WSopcode_t + * @param length size_t length of the payload + * @param mask bool add dummy mask to the frame (needed for web browser) + * @param maskkey uint8_t[4] key used for payload + * @param fin bool can be used to send data in more then one frame (set fin on the last frame) + */ +uint8_t WebSockets::createHeader(uint8_t * headerPtr, WSopcode_t opcode, size_t length, bool mask, uint8_t maskKey[4], bool fin) { + uint8_t headerSize; + // calculate header Size + if(length < 126) { + headerSize = 2; + } else if(length < 0xFFFF) { + headerSize = 4; + } else { + headerSize = 10; + } + + if(mask) { + headerSize += 4; + } + + // create header + + // byte 0 + *headerPtr = 0x00; + if(fin) { + *headerPtr |= bit(7); ///< set Fin + } + *headerPtr |= opcode; ///< set opcode + headerPtr++; + + // byte 1 + *headerPtr = 0x00; + if(mask) { + *headerPtr |= bit(7); ///< set mask + } + + if(length < 126) { + *headerPtr |= length; + headerPtr++; + } else if(length < 0xFFFF) { + *headerPtr |= 126; + headerPtr++; + *headerPtr = ((length >> 8) & 0xFF); + headerPtr++; + *headerPtr = (length & 0xFF); + headerPtr++; + } else { + // Normally we never get here (to less memory) + *headerPtr |= 127; + headerPtr++; + *headerPtr = 0x00; + headerPtr++; + *headerPtr = 0x00; + headerPtr++; + *headerPtr = 0x00; + headerPtr++; + *headerPtr = 0x00; + headerPtr++; + *headerPtr = ((length >> 24) & 0xFF); + headerPtr++; + *headerPtr = ((length >> 16) & 0xFF); + headerPtr++; + *headerPtr = ((length >> 8) & 0xFF); + headerPtr++; + *headerPtr = (length & 0xFF); + headerPtr++; + } + + if(mask) { + *headerPtr = maskKey[0]; + headerPtr++; + *headerPtr = maskKey[1]; + headerPtr++; + *headerPtr = maskKey[2]; + headerPtr++; + *headerPtr = maskKey[3]; + headerPtr++; + } + return headerSize; +} + +/** + * + * @param client WSclient_t * ptr to the client struct + * @param opcode WSopcode_t + * @param length size_t length of the payload + * @param mask bool add dummy mask to the frame (needed for web browser) + * @param fin bool can be used to send data in more then one frame (set fin on the last frame) + * @return true if ok + */ +bool WebSockets::sendFrameHeader(WSclient_t * client, WSopcode_t opcode, size_t length, bool mask, bool fin) { + uint8_t maskKey[4] = { 0x00, 0x00, 0x00, 0x00 }; + uint8_t buffer[WEBSOCKETS_MAX_HEADER_SIZE] = { 0 }; + + uint8_t headerSize = createHeader(&buffer[0], opcode, length, mask, maskKey, fin); + + if(write(client, &buffer[0], headerSize) != headerSize) { + return false; + } + + return true; +} + + + /** * * @param client WSclient_t * ptr to the client struct @@ -146,53 +255,7 @@ bool WebSockets::sendFrame(WSclient_t * client, WSopcode_t opcode, uint8_t * pay headerPtr = &buffer[0]; } - // create header - - // byte 0 - *headerPtr = 0x00; - if(fin) { - *headerPtr |= bit(7); ///< set Fin - } - *headerPtr |= opcode; ///< set opcode - headerPtr++; - - // byte 1 - *headerPtr = 0x00; - if(mask) { - *headerPtr |= bit(7); ///< set mask - } - - if(length < 126) { - *headerPtr |= length; - headerPtr++; - } else if(length < 0xFFFF) { - *headerPtr |= 126; - headerPtr++; - *headerPtr = ((length >> 8) & 0xFF); - headerPtr++; - *headerPtr = (length & 0xFF); - headerPtr++; - } else { - // Normally we never get here (to less memory) - *headerPtr |= 127; - headerPtr++; - *headerPtr = 0x00; - headerPtr++; - *headerPtr = 0x00; - headerPtr++; - *headerPtr = 0x00; - headerPtr++; - *headerPtr = 0x00; - headerPtr++; - *headerPtr = ((length >> 24) & 0xFF); - headerPtr++; - *headerPtr = ((length >> 16) & 0xFF); - headerPtr++; - *headerPtr = ((length >> 8) & 0xFF); - headerPtr++; - *headerPtr = (length & 0xFF); - headerPtr++; - } + createHeader(headerPtr, opcode, length, mask, maskKey, fin); if(mask) { if(useInternBuffer) { @@ -200,8 +263,6 @@ bool WebSockets::sendFrame(WSclient_t * client, WSopcode_t opcode, uint8_t * pay // by this fact its possible the do the masking for(uint8_t x = 0; x < sizeof(maskKey); x++) { maskKey[x] = random(0xFF); - *headerPtr = maskKey[x]; - headerPtr++; } uint8_t * dataMaskPtr; @@ -215,16 +276,6 @@ bool WebSockets::sendFrame(WSclient_t * client, WSopcode_t opcode, uint8_t * pay for(size_t x = 0; x < length; x++) { dataMaskPtr[x] = (dataMaskPtr[x] ^ maskKey[x % 4]); } - - } else { - *headerPtr = maskKey[0]; - headerPtr++; - *headerPtr = maskKey[1]; - headerPtr++; - *headerPtr = maskKey[2]; - headerPtr++; - *headerPtr = maskKey[3]; - headerPtr++; } } diff --git a/src/WebSockets.h b/src/WebSockets.h index 4063b5f..ebcf612 100644 --- a/src/WebSockets.h +++ b/src/WebSockets.h @@ -285,6 +285,9 @@ class WebSockets { virtual void messageReceived(WSclient_t * client, WSopcode_t opcode, uint8_t * payload, size_t length, bool fin) = 0; void clientDisconnect(WSclient_t * client, uint16_t code, char * reason = NULL, size_t reasonLen = 0); + + uint8_t createHeader(uint8_t * buf, WSopcode_t opcode, size_t length, bool mask, uint8_t maskKey[4], bool fin); + bool sendFrameHeader(WSclient_t * client, WSopcode_t opcode, size_t length = 0, bool mask = false, bool fin = true); bool sendFrame(WSclient_t * client, WSopcode_t opcode, uint8_t * payload = NULL, size_t length = 0, bool mask = false, bool fin = true, bool headerToPayload = false); void headerDone(WSclient_t * client); diff --git a/src/WebSocketsClient.cpp b/src/WebSocketsClient.cpp index d5e6f47..2e2faf3 100644 --- a/src/WebSocketsClient.cpp +++ b/src/WebSocketsClient.cpp @@ -310,6 +310,10 @@ void WebSocketsClient::setReconnectInterval(unsigned long time) { _reconnectInterval = time; } +bool WebSocketsClient::isConnected(void) { + return (_client.status == WSC_CONNECTED); +} + //################################################################################# //################################################################################# //################################################################################# @@ -666,6 +670,13 @@ void WebSocketsClient::handleHeader(WSclient_t * client, String * headerLine) { runCbEvent(WStype_CONNECTED, (uint8_t *) client->cUrl.c_str(), client->cUrl.length()); } else if(clientIsConnected(client) && client->isSocketIO && client->cSessionId.length() > 0) { + if(_client.tcp->available()) { + // read not needed data + DEBUG_WEBSOCKETS("[WS-Client][handleHeader] still data in buffer (%d), clean up.\n", _client.tcp->available()); + while(_client.tcp->available() > 0) { + _client.tcp->read(); + } + } sendHeader(client); } else { DEBUG_WEBSOCKETS("[WS-Client][handleHeader] no Websocket connection close.\n"); diff --git a/src/WebSocketsClient.h b/src/WebSocketsClient.h index db02300..711ce92 100644 --- a/src/WebSocketsClient.h +++ b/src/WebSocketsClient.h @@ -27,7 +27,7 @@ #include "WebSockets.h" -class WebSocketsClient: private WebSockets { +class WebSocketsClient: protected WebSockets { public: #ifdef __AVR__ typedef void (*WebSocketClientEvent)(WStype_t type, uint8_t * payload, size_t length); @@ -87,6 +87,8 @@ class WebSocketsClient: private WebSockets { void setReconnectInterval(unsigned long time); + bool isConnected(void); + protected: String _host; uint16_t _port; From 50903cd410832ba5cd482b72651a7edebdbc4dc2 Mon Sep 17 00:00:00 2001 From: Links Date: Sat, 12 May 2018 17:10:17 +0200 Subject: [PATCH 04/11] basic event RX working --- src/SocketIOclient.cpp | 26 +++++++++++++++++++++++++- 1 file changed, 25 insertions(+), 1 deletion(-) diff --git a/src/SocketIOclient.cpp b/src/SocketIOclient.cpp index d2b2f28..845afb3 100644 --- a/src/SocketIOclient.cpp +++ b/src/SocketIOclient.cpp @@ -118,9 +118,33 @@ void SocketIOclient::runCbEvent(WStype_t type, uint8_t * payload, size_t length) case eIOtype_PONG: DEBUG_WEBSOCKETS("[wsIOc] get pong\n"); break; + case eIOtype_MESSAGE: { + if(length < 2) { + break; + } + socketIOmessageType_t ioType = (socketIOmessageType_t) payload[1]; + uint8_t * data = &payload[2]; + size_t lData = length - 2; + switch(ioType) { + case sIOtype_EVENT: + DEBUG_WEBSOCKETS("[wsIOc] get event (%d): %s\n", lData, data); + break; + case sIOtype_CONNECT: + case sIOtype_DISCONNECT: + case sIOtype_ACK: + case sIOtype_ERROR: + case sIOtype_BINARY_EVENT: + case sIOtype_BINARY_ACK: + default: + DEBUG_WEBSOCKETS("[wsIOc] Socket.IO Message Type %c (%02X) is not implemented\n", ioType, ioType); + DEBUG_WEBSOCKETS("[wsIOc] get text: %s\n", payload); + break; + } + + } + break; case eIOtype_OPEN: case eIOtype_CLOSE: - case eIOtype_MESSAGE: case eIOtype_UPGRADE: case eIOtype_NOOP: default: From dfa35fa9ae11b98fb071bb4aef086cb4a6a7f27d Mon Sep 17 00:00:00 2001 From: Links Date: Mon, 10 Jun 2019 13:31:51 +0200 Subject: [PATCH 05/11] clang-format --- src/SocketIOclient.cpp | 37 ++++++++++------------- src/SocketIOclient.h | 68 ++++++++++++++++++++---------------------- 2 files changed, 48 insertions(+), 57 deletions(-) diff --git a/src/SocketIOclient.cpp b/src/SocketIOclient.cpp index 1ce8d0c..3609583 100644 --- a/src/SocketIOclient.cpp +++ b/src/SocketIOclient.cpp @@ -10,14 +10,12 @@ #include "SocketIOclient.h" SocketIOclient::SocketIOclient() { - } SocketIOclient::~SocketIOclient() { - } -void SocketIOclient::begin(const char *host, uint16_t port, const char * url, const char * protocol) { +void SocketIOclient::begin(const char * host, uint16_t port, const char * url, const char * protocol) { WebSocketsClient::beginSocketIO(host, port, url, protocol); } @@ -40,20 +38,19 @@ bool SocketIOclient::isConnected(void) { bool SocketIOclient::sendEVENT(uint8_t * payload, size_t length, bool headerToPayload) { bool ret = false; if(length == 0) { - length = strlen((const char *) payload); + length = strlen((const char *)payload); } if(clientIsConnected(&_client)) { - if(!headerToPayload) { // webSocket Header ret = WebSocketsClient::sendFrameHeader(&_client, WSop_text, length + 2, true); // Engine.IO / Socket.IO Header if(ret) { uint8_t buf[3] = { eIOtype_MESSAGE, sIOtype_EVENT, 0x00 }; - ret = WebSocketsClient::write(&_client, buf, 2); + ret = WebSocketsClient::write(&_client, buf, 2); } if(ret) { - ret = WebSocketsClient::write(&_client, payload, length ); + ret = WebSocketsClient::write(&_client, payload, length); } return ret; } else { @@ -66,19 +63,19 @@ bool SocketIOclient::sendEVENT(uint8_t * payload, size_t length, bool headerToPa } bool SocketIOclient::sendEVENT(const uint8_t * payload, size_t length) { - return sendEVENT((uint8_t *) payload, length); + return sendEVENT((uint8_t *)payload, length); } bool SocketIOclient::sendEVENT(char * payload, size_t length, bool headerToPayload) { - return sendEVENT((uint8_t *) payload, length, headerToPayload); + return sendEVENT((uint8_t *)payload, length, headerToPayload); } bool SocketIOclient::sendEVENT(const char * payload, size_t length) { - return sendEVENT((uint8_t *) payload, length); + return sendEVENT((uint8_t *)payload, length); } bool SocketIOclient::sendEVENT(String & payload) { - return sendEVENT((uint8_t *) payload.c_str(), payload.length()); + return sendEVENT((uint8_t *)payload.c_str(), payload.length()); } void SocketIOclient::loop(void) { @@ -100,15 +97,13 @@ void SocketIOclient::runCbEvent(WStype_t type, uint8_t * payload, size_t length) // send message to server when Connected // Engine.io upgrade confirmation message (required) WebSocketsClient::sendTXT(eIOtype_UPGRADE); - } - break; + } break; case WStype_TEXT: { - if(length < 1) { break; } - engineIOmessageType_t eType = (engineIOmessageType_t) payload[0]; + engineIOmessageType_t eType = (engineIOmessageType_t)payload[0]; switch(eType) { case eIOtype_PING: payload[0] = eIOtype_PONG; @@ -122,9 +117,9 @@ void SocketIOclient::runCbEvent(WStype_t type, uint8_t * payload, size_t length) if(length < 2) { break; } - socketIOmessageType_t ioType = (socketIOmessageType_t) payload[1]; - uint8_t * data = &payload[2]; - size_t lData = length - 2; + socketIOmessageType_t ioType = (socketIOmessageType_t)payload[1]; + uint8_t * data = &payload[2]; + size_t lData = length - 2; switch(ioType) { case sIOtype_EVENT: DEBUG_WEBSOCKETS("[wsIOc] get event (%d): %s\n", lData, data); @@ -141,8 +136,7 @@ void SocketIOclient::runCbEvent(WStype_t type, uint8_t * payload, size_t length) break; } - } - break; + } break; case eIOtype_OPEN: case eIOtype_CLOSE: case eIOtype_UPGRADE: @@ -155,8 +149,7 @@ void SocketIOclient::runCbEvent(WStype_t type, uint8_t * payload, size_t length) // send message to server // webSocket.sendTXT("message here"); - } - break; + } break; case WStype_BIN: DEBUG_WEBSOCKETS("[wsIOc] get binary length: %u\n", length); // hexdump(payload, length); diff --git a/src/SocketIOclient.h b/src/SocketIOclient.h index b179954..785d038 100644 --- a/src/SocketIOclient.h +++ b/src/SocketIOclient.h @@ -12,58 +12,56 @@ #define EIO_HEARTBEAT_INTERVAL 10000 -#define EIO_MAX_HEADER_SIZE (WEBSOCKETS_MAX_HEADER_SIZE + 1) -#define SIO_MAX_HEADER_SIZE (EIO_MAX_HEADER_SIZE + 1) +#define EIO_MAX_HEADER_SIZE (WEBSOCKETS_MAX_HEADER_SIZE + 1) +#define SIO_MAX_HEADER_SIZE (EIO_MAX_HEADER_SIZE + 1) typedef enum { - eIOtype_OPEN = '0', ///< Sent from the server when a new transport is opened (recheck) - eIOtype_CLOSE = '1', ///< Request the close of this transport but does not shutdown the connection itself. - eIOtype_PING = '2', ///< Sent by the client. Server should answer with a pong packet containing the same data - eIOtype_PONG = '3', ///< Sent by the server to respond to ping packets. - eIOtype_MESSAGE = '4', ///< actual message, client and server should call their callbacks with the data - eIOtype_UPGRADE = '5', ///< Before engine.io switches a transport, it tests, if server and client can communicate over this transport. If this test succeed, the client sends an upgrade packets which requests the server to flush its cache on the old transport and switch to the new transport. - eIOtype_NOOP = '6', ///< A noop packet. Used primarily to force a poll cycle when an incoming websocket connection is received. + eIOtype_OPEN = '0', ///< Sent from the server when a new transport is opened (recheck) + eIOtype_CLOSE = '1', ///< Request the close of this transport but does not shutdown the connection itself. + eIOtype_PING = '2', ///< Sent by the client. Server should answer with a pong packet containing the same data + eIOtype_PONG = '3', ///< Sent by the server to respond to ping packets. + eIOtype_MESSAGE = '4', ///< actual message, client and server should call their callbacks with the data + eIOtype_UPGRADE = '5', ///< Before engine.io switches a transport, it tests, if server and client can communicate over this transport. If this test succeed, the client sends an upgrade packets which requests the server to flush its cache on the old transport and switch to the new transport. + eIOtype_NOOP = '6', ///< A noop packet. Used primarily to force a poll cycle when an incoming websocket connection is received. } engineIOmessageType_t; - typedef enum { - sIOtype_CONNECT = '0', - sIOtype_DISCONNECT = '1', - sIOtype_EVENT = '2', - sIOtype_ACK = '3', - sIOtype_ERROR = '4', + sIOtype_CONNECT = '0', + sIOtype_DISCONNECT = '1', + sIOtype_EVENT = '2', + sIOtype_ACK = '3', + sIOtype_ERROR = '4', sIOtype_BINARY_EVENT = '5', - sIOtype_BINARY_ACK = '6', + sIOtype_BINARY_ACK = '6', } socketIOmessageType_t; -class SocketIOclient: protected WebSocketsClient { - - public: +class SocketIOclient : protected WebSocketsClient { + public: #ifdef __AVR__ - typedef void (*SocketIOclientEvent)(WStype_t type, uint8_t * payload, size_t length); + typedef void (*SocketIOclientEvent)(WStype_t type, uint8_t * payload, size_t length); #else - typedef std::function SocketIOclientEvent; + typedef std::function SocketIOclientEvent; #endif - SocketIOclient(void); - virtual ~SocketIOclient(void); + SocketIOclient(void); + virtual ~SocketIOclient(void); - void begin(const char *host, uint16_t port, const char * url = "/socket.io/?EIO=3", const char * protocol = "arduino"); - void begin(String host, uint16_t port, String url = "/socket.io/?EIO=3", String protocol = "arduino"); + void begin(const char * host, uint16_t port, const char * url = "/socket.io/?EIO=3", const char * protocol = "arduino"); + void begin(String host, uint16_t port, String url = "/socket.io/?EIO=3", String protocol = "arduino"); - bool isConnected(void); + bool isConnected(void); - bool sendEVENT(uint8_t * payload, size_t length = 0, bool headerToPayload = false); - bool sendEVENT(const uint8_t * payload, size_t length = 0); - bool sendEVENT(char * payload, size_t length = 0, bool headerToPayload = false); - bool sendEVENT(const char * payload, size_t length = 0); - bool sendEVENT(String & payload); + bool sendEVENT(uint8_t * payload, size_t length = 0, bool headerToPayload = false); + bool sendEVENT(const uint8_t * payload, size_t length = 0); + bool sendEVENT(char * payload, size_t length = 0, bool headerToPayload = false); + bool sendEVENT(const char * payload, size_t length = 0); + bool sendEVENT(String & payload); - void loop(void); + void loop(void); - protected: - void runCbEvent(WStype_t type, uint8_t * payload, size_t length); - uint64_t _lastHeartbeat = 0; + protected: + void runCbEvent(WStype_t type, uint8_t * payload, size_t length); + uint64_t _lastHeartbeat = 0; }; #endif /* SOCKETIOCLIENT_H_ */ From dbca2ab420076ff86fa2f1ab0324e9e10169bd3a Mon Sep 17 00:00:00 2001 From: Links Date: Mon, 10 Jun 2019 14:57:49 +0200 Subject: [PATCH 06/11] SocketIO message RX is working --- src/SocketIOclient.cpp | 18 ++++++++++++++++-- src/SocketIOclient.h | 23 ++++++++++++++++++----- 2 files changed, 34 insertions(+), 7 deletions(-) diff --git a/src/SocketIOclient.cpp b/src/SocketIOclient.cpp index 3609583..3d51568 100644 --- a/src/SocketIOclient.cpp +++ b/src/SocketIOclient.cpp @@ -17,10 +17,20 @@ SocketIOclient::~SocketIOclient() { void SocketIOclient::begin(const char * host, uint16_t port, const char * url, const char * protocol) { WebSocketsClient::beginSocketIO(host, port, url, protocol); + WebSocketsClient::enableHeartbeat(60 * 1000, 90 * 1000, 5); } void SocketIOclient::begin(String host, uint16_t port, String url, String protocol) { WebSocketsClient::beginSocketIO(host, port, url, protocol); + WebSocketsClient::enableHeartbeat(60 * 1000, 90 * 1000, 5); +} + +/** + * set callback function + * @param cbEvent SocketIOclientEvent + */ +void SocketIOclient::onEvent(SocketIOclientEvent cbEvent) { + _cbEvent = cbEvent; } bool SocketIOclient::isConnected(void) { @@ -83,13 +93,15 @@ void SocketIOclient::loop(void) { unsigned long t = millis(); if((t - _lastConnectionFail) > EIO_HEARTBEAT_INTERVAL) { _lastConnectionFail = t; - //WebSocketsClient::sendTXT(eIOtype_PING); + DEBUG_WEBSOCKETS("[wsIOc] send ping\n"); + WebSocketsClient::sendTXT(eIOtype_PING); } } -void SocketIOclient::runCbEvent(WStype_t type, uint8_t * payload, size_t length) { +void SocketIOclient::handleCbEvent(WStype_t type, uint8_t * payload, size_t length) { switch(type) { case WStype_DISCONNECTED: + runIOCbEvent(sIOtype_DISCONNECT, NULL, 0); DEBUG_WEBSOCKETS("[wsIOc] Disconnected!\n"); break; case WStype_CONNECTED: { @@ -97,6 +109,7 @@ void SocketIOclient::runCbEvent(WStype_t type, uint8_t * payload, size_t length) // send message to server when Connected // Engine.io upgrade confirmation message (required) WebSocketsClient::sendTXT(eIOtype_UPGRADE); + runIOCbEvent(sIOtype_CONNECT, payload, length); } break; case WStype_TEXT: { if(length < 1) { @@ -136,6 +149,7 @@ void SocketIOclient::runCbEvent(WStype_t type, uint8_t * payload, size_t length) break; } + runIOCbEvent(ioType, data, lData); } break; case eIOtype_OPEN: case eIOtype_CLOSE: diff --git a/src/SocketIOclient.h b/src/SocketIOclient.h index 785d038..64b8c87 100644 --- a/src/SocketIOclient.h +++ b/src/SocketIOclient.h @@ -1,4 +1,4 @@ -/* +/** * SocketIOclient.h * * Created on: May 12, 2018 @@ -10,7 +10,7 @@ #include "WebSockets.h" -#define EIO_HEARTBEAT_INTERVAL 10000 +#define EIO_HEARTBEAT_INTERVAL 20000 #define EIO_MAX_HEADER_SIZE (WEBSOCKETS_MAX_HEADER_SIZE + 1) #define SIO_MAX_HEADER_SIZE (EIO_MAX_HEADER_SIZE + 1) @@ -38,9 +38,9 @@ typedef enum { class SocketIOclient : protected WebSocketsClient { public: #ifdef __AVR__ - typedef void (*SocketIOclientEvent)(WStype_t type, uint8_t * payload, size_t length); + typedef void (*SocketIOclientEvent)(socketIOmessageType_t type, uint8_t * payload, size_t length); #else - typedef std::function SocketIOclientEvent; + typedef std::function SocketIOclientEvent; #endif SocketIOclient(void); @@ -51,6 +51,8 @@ class SocketIOclient : protected WebSocketsClient { bool isConnected(void); + void onEvent(SocketIOclientEvent cbEvent); + bool sendEVENT(uint8_t * payload, size_t length = 0, bool headerToPayload = false); bool sendEVENT(const uint8_t * payload, size_t length = 0); bool sendEVENT(char * payload, size_t length = 0, bool headerToPayload = false); @@ -60,8 +62,19 @@ class SocketIOclient : protected WebSocketsClient { void loop(void); protected: - void runCbEvent(WStype_t type, uint8_t * payload, size_t length); uint64_t _lastHeartbeat = 0; + SocketIOclientEvent _cbEvent; + virtual void runIOCbEvent(socketIOmessageType_t type, uint8_t * payload, size_t length) { + if(_cbEvent) { + _cbEvent(type, payload, length); + } + } + + // Handeling events from websocket layer + virtual void runCbEvent(WStype_t type, uint8_t * payload, size_t length) { + handleCbEvent(type, payload, length); + } + void handleCbEvent(WStype_t type, uint8_t * payload, size_t length); }; #endif /* SOCKETIOCLIENT_H_ */ From 007b5ba9a859d3ac3e2011bc7bafcf951b55b658 Mon Sep 17 00:00:00 2001 From: Links Date: Mon, 10 Jun 2019 15:13:26 +0200 Subject: [PATCH 07/11] ignore not needed WS events for SocketIO --- src/SocketIOclient.cpp | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/src/SocketIOclient.cpp b/src/SocketIOclient.cpp index 3d51568..755bc9b 100644 --- a/src/SocketIOclient.cpp +++ b/src/SocketIOclient.cpp @@ -160,16 +160,15 @@ void SocketIOclient::handleCbEvent(WStype_t type, uint8_t * payload, size_t leng DEBUG_WEBSOCKETS("[wsIOc] get text: %s\n", payload); break; } - - // send message to server - // webSocket.sendTXT("message here"); } break; - case WStype_BIN: - DEBUG_WEBSOCKETS("[wsIOc] get binary length: %u\n", length); - // hexdump(payload, length); - // send data to server - // webSocket.sendBIN(payload, length); + case WStype_BIN: + case WStype_FRAGMENT_TEXT_START: + case WStype_FRAGMENT_BIN_START: + case WStype_FRAGMENT: + case WStype_FRAGMENT_FIN: + case WStype_PING: + case WStype_PONG: break; } } From 5d26a65df452be50361c6dd90098112afcc04eb0 Mon Sep 17 00:00:00 2001 From: Links Date: Thu, 20 Jun 2019 10:02:07 +0200 Subject: [PATCH 08/11] only send payload when we have a ptr and length --- src/SocketIOclient.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/SocketIOclient.cpp b/src/SocketIOclient.cpp index 755bc9b..e9467d4 100644 --- a/src/SocketIOclient.cpp +++ b/src/SocketIOclient.cpp @@ -59,7 +59,7 @@ bool SocketIOclient::sendEVENT(uint8_t * payload, size_t length, bool headerToPa uint8_t buf[3] = { eIOtype_MESSAGE, sIOtype_EVENT, 0x00 }; ret = WebSocketsClient::write(&_client, buf, 2); } - if(ret) { + if(ret && payload && length > 0) { ret = WebSocketsClient::write(&_client, payload, length); } return ret; From adf2b07f3bc9495beaa3e5755671482ded8156d8 Mon Sep 17 00:00:00 2001 From: Links Date: Sun, 23 Jun 2019 12:14:44 +0200 Subject: [PATCH 09/11] fix #447 --- src/WebSocketsClient.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/WebSocketsClient.cpp b/src/WebSocketsClient.cpp index f593692..db15e5b 100644 --- a/src/WebSocketsClient.cpp +++ b/src/WebSocketsClient.cpp @@ -762,7 +762,7 @@ void WebSocketsClient::connectedCb() { return; } } else if(_client.isSSL && !_CA_cert) { -#if defined(wificlientbearssl_h) && !defined(USING_AXTLS) +#if defined(wificlientbearssl_h) && !defined(USING_AXTLS) && !defined(wificlientsecure_h) _client.ssl->setInsecure(); #endif } From 7816bb3fa48683c065d5519969fbdc5563a5d63f Mon Sep 17 00:00:00 2001 From: Links Date: Sat, 29 Jun 2019 18:31:14 +0200 Subject: [PATCH 10/11] adding example for new socket.IO support --- .travis.yml | 4 + .../WebSocketClientSocketIO.ino | 113 ++++++++++-------- 2 files changed, 66 insertions(+), 51 deletions(-) diff --git a/.travis.yml b/.travis.yml index 9d3f31b..c319bab 100644 --- a/.travis.yml +++ b/.travis.yml @@ -28,6 +28,10 @@ script: - export PATH="$HOME/arduino_ide:$PATH" - which arduino - mkdir -p $HOME/Arduino/libraries + + - wget https://github.com/bblanchon/ArduinoJson/archive/6.x.zip + - unzip 6.x.zip + - mv ArduinoJson-6.x $HOME/Arduino/libraries/ArduinoJson - cp -r $TRAVIS_BUILD_DIR $HOME/Arduino/libraries/arduinoWebSockets - source $TRAVIS_BUILD_DIR/travis/common.sh - get_core $CPU diff --git a/examples/esp8266/WebSocketClientSocketIO/WebSocketClientSocketIO.ino b/examples/esp8266/WebSocketClientSocketIO/WebSocketClientSocketIO.ino index 39dce87..52aef19 100644 --- a/examples/esp8266/WebSocketClientSocketIO/WebSocketClientSocketIO.ino +++ b/examples/esp8266/WebSocketClientSocketIO/WebSocketClientSocketIO.ino @@ -10,56 +10,45 @@ #include #include +#include + #include #include ESP8266WiFiMulti WiFiMulti; -WebSocketsClient webSocket; - +SocketIOclient socketIO; #define USE_SERIAL Serial1 -#define MESSAGE_INTERVAL 30000 -#define HEARTBEAT_INTERVAL 25000 - -uint64_t messageTimestamp = 0; -uint64_t heartbeatTimestamp = 0; -bool isConnected = false; - -void webSocketEvent(WStype_t type, uint8_t * payload, size_t length) { - - +void socketIOEvent(socketIOmessageType_t type, uint8_t * payload, size_t length) { switch(type) { - case WStype_DISCONNECTED: - USE_SERIAL.printf("[WSc] Disconnected!\n"); - isConnected = false; + case sIOtype_DISCONNECT: + USE_SERIAL.printf("[IOc] Disconnected!\n"); break; - case WStype_CONNECTED: - { - USE_SERIAL.printf("[WSc] Connected to url: %s\n", payload); - isConnected = true; - - // send message to server when Connected - // socket.io upgrade confirmation message (required) - webSocket.sendTXT("5"); - } + case sIOtype_CONNECT: + USE_SERIAL.printf("[IOc] Connected to url: %s\n", payload); break; - case WStype_TEXT: - USE_SERIAL.printf("[WSc] get text: %s\n", payload); - - // send message to server - // webSocket.sendTXT("message here"); + case sIOtype_EVENT: + USE_SERIAL.printf("[IOc] get event: %s\n", payload); break; - case WStype_BIN: - USE_SERIAL.printf("[WSc] get binary length: %u\n", length); + case sIOtype_ACK: + USE_SERIAL.printf("[IOc] get ack: %u\n", length); + hexdump(payload, length); + break; + case sIOtype_ERROR: + USE_SERIAL.printf("[IOc] get error: %u\n", length); + hexdump(payload, length); + break; + case sIOtype_BINARY_EVENT: + USE_SERIAL.printf("[IOc] get binary: %u\n", length); + hexdump(payload, length); + break; + case sIOtype_BINARY_ACK: + USE_SERIAL.printf("[IOc] get binary ack: %u\n", length); hexdump(payload, length); - - // send data to server - // webSocket.sendBIN(payload, length); break; } - } void setup() { @@ -79,6 +68,11 @@ void setup() { delay(1000); } + // disable AP + if(WiFi.getMode() & WIFI_AP) { + WiFi.softAPdisconnect(true); + } + WiFiMulti.addAP("SSID", "passpasspass"); //WiFi.disconnect(); @@ -86,28 +80,45 @@ void setup() { delay(100); } - webSocket.beginSocketIO("192.168.0.123", 81); - //webSocket.setAuthorization("user", "Password"); // HTTP Basic Authorization - webSocket.onEvent(webSocketEvent); + String ip = WiFi.localIP().toString(); + USE_SERIAL.printf("[SETUP] WiFi Connected %s\n", ip.c_str()); + // server address, port and URL + socketIO.begin("10.11.100.100", 8880); + + // event handler + socketIO.onEvent(socketIOEvent); } +unsigned long messageTimestamp = 0; void loop() { - webSocket.loop(); + socketIO.loop(); - if(isConnected) { + uint64_t now = millis(); - uint64_t now = millis(); + if(now - messageTimestamp > 2000) { + messageTimestamp = now; - if(now - messageTimestamp > MESSAGE_INTERVAL) { - messageTimestamp = now; - // example socket.io message with type "messageType" and JSON payload - webSocket.sendTXT("42[\"messageType\",{\"greeting\":\"hello\"}]"); - } - if((now - heartbeatTimestamp) > HEARTBEAT_INTERVAL) { - heartbeatTimestamp = now; - // socket.io heartbeat message - webSocket.sendTXT("2"); - } + // creat JSON message for Socket.IO (event) + DynamicJsonDocument doc(1024); + JsonArray array = doc.to(); + + // add evnet name + // Hint: socket.on('event_name', .... + array.add("event_name"); + + // add payload (parameters) for the event + JsonObject param1 = array.createNestedObject(); + param1["now"] = now; + + // JSON to String (serializion) + String output; + serializeJson(doc, output); + + // Send event + socketIO.sendEVENT(output); + + // Print JSON for debugging + USE_SERIAL.println(output); } } From 4ef8d733dcd443b2ae63d39cc551b782e250c1e2 Mon Sep 17 00:00:00 2001 From: Links Date: Sat, 29 Jun 2019 18:35:21 +0200 Subject: [PATCH 11/11] add missing include --- .../esp8266/WebSocketClientSocketIO/WebSocketClientSocketIO.ino | 1 + 1 file changed, 1 insertion(+) diff --git a/examples/esp8266/WebSocketClientSocketIO/WebSocketClientSocketIO.ino b/examples/esp8266/WebSocketClientSocketIO/WebSocketClientSocketIO.ino index 52aef19..343b5f5 100644 --- a/examples/esp8266/WebSocketClientSocketIO/WebSocketClientSocketIO.ino +++ b/examples/esp8266/WebSocketClientSocketIO/WebSocketClientSocketIO.ino @@ -13,6 +13,7 @@ #include #include +#include #include