/** * @file BlynkSocket.h * @author Volodymyr Shymanskyy * @license This project is released under the MIT License (MIT) * @copyright Copyright (c) 2015 Volodymyr Shymanskyy * @date Mar 2015 * @brief */ #ifndef BlynkSocket_h #define BlynkSocket_h #include #include #include #include #include #include #include #include #include #include #include #include #include class BlynkTransportSocket { public: BlynkTransportSocket() : sockfd(-1), domain(NULL), port(0) {} void begin(const char* h, uint16_t p) { this->domain = h; this->port = p; } bool connect() { BLYNK_LOG4(BLYNK_F("Connecting to "), domain, ':', port); struct addrinfo hints; struct addrinfo *res = NULL; // will point to the results memset(&hints, 0, sizeof hints); // make sure the struct is empty hints.ai_family = AF_UNSPEC; // don't care IPv4 or IPv6 hints.ai_socktype = SOCK_STREAM; // TCP stream sockets // get ready to connect char port_str[8]; snprintf(port_str, sizeof(port_str), "%u", port); getaddrinfo(domain, port_str, &hints, &res); if (res == NULL) { BLYNK_LOG1(BLYNK_F("Cannot get addr info")); return false; } if ((sockfd = ::socket(res->ai_family, res->ai_socktype, res->ai_protocol)) < 0) { BLYNK_LOG1(BLYNK_F("Can't create socket")); return false; } if (::connect(sockfd, res->ai_addr, res->ai_addrlen) < 0) { BLYNK_LOG2(BLYNK_F("Can't connect to "), domain); return false; } struct timeval tv; tv.tv_sec = 0; tv.tv_usec = 1000; setsockopt(sockfd, SOL_SOCKET, SO_RCVTIMEO, (char *)&tv, sizeof(struct timeval)); int one = 1; setsockopt(sockfd, SOL_TCP, TCP_NODELAY, &one, sizeof(one)); freeaddrinfo(res); // TODO: Leak here return true; } void disconnect() { if (sockfd != -1) { while (::close(sockfd) < 0) { usleep(10000); } sockfd = -1; } } size_t read(void* buf, size_t len) { ssize_t rlen = ::read(sockfd, buf, len); if (rlen == -1) { //BLYNK_LOG4("Read error ", errno, ": ", strerror(errno)); if (errno == ETIMEDOUT || errno == EWOULDBLOCK || errno == EAGAIN) { return 0; } disconnect(); return -1; } return rlen; } size_t write(const void* buf, size_t len) { return ::write(sockfd, buf, len); } bool connected() { return sockfd >= 0; } int available() { if (!connected()) { return 0; } int count = 0; if (0 == ioctl(sockfd, FIONREAD, &count)) { if (!count) { usleep(10000); // not to stall CPU with 100% load } return count; } return 0; } protected: int sockfd; const char* domain; uint16_t port; }; class BlynkSocket : public BlynkProtocol { typedef BlynkProtocol Base; public: BlynkSocket(BlynkTransportSocket& transp) : Base(transp) {} void begin(const char* auth, const char* domain = BLYNK_DEFAULT_DOMAIN, uint16_t port = BLYNK_DEFAULT_PORT) { Base::begin(auth); this->conn.begin(domain, port); } }; #endif