From f95c0143424995d4b16e7d3ea6e2b07554eae890 Mon Sep 17 00:00:00 2001 From: Links Date: Sat, 12 May 2018 16:53:34 +0200 Subject: [PATCH] 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;