diff --git a/drivers/Linux/EthernetClient.cpp b/drivers/Linux/EthernetClient.cpp index 640000b..8540a62 100644 --- a/drivers/Linux/EthernetClient.cpp +++ b/drivers/Linux/EthernetClient.cpp @@ -19,9 +19,11 @@ * Based on Arduino ethernet library, Copyright (c) 2010 Arduino LLC. All right reserved. */ +#include "EthernetClient.h" #include #include #include +#include #include #include #include @@ -30,7 +32,6 @@ #include #include #include "log.h" -#include "EthernetClient.h" EthernetClient::EthernetClient() : _sock(-1) { @@ -67,7 +68,7 @@ int EthernetClient::connect(const char* host, uint16_t port) } if (::connect(sockfd, p->ai_addr, p->ai_addrlen) == -1) { - close(sockfd); + ::close(sockfd); logError("connect: %s\n", strerror(errno)); continue; } @@ -113,8 +114,7 @@ size_t EthernetClient::write(const uint8_t *buf, size_t size) int rc = send(_sock, buf + bytes, size, MSG_NOSIGNAL | MSG_DONTWAIT); if (rc == -1) { logError("send: %s\n", strerror(errno)); - close(_sock); - _sock = -1; + close(); break; } bytes += rc; @@ -141,16 +141,16 @@ int EthernetClient::available() int count = 0; if (_sock != -1) { - ioctl(_sock, FIONREAD, &count); - return count; + ioctl(_sock, SIOCINQ, &count); } - return 0; + + return count; } int EthernetClient::read() { uint8_t b; - if ( recv(_sock, &b, 1, 0) > 0 ) { + if ( recv(_sock, &b, 1, MSG_DONTWAIT) > 0 ) { // recv worked return b; } else { @@ -159,22 +159,35 @@ int EthernetClient::read() } } -int EthernetClient::read(uint8_t *buf, size_t size) +int EthernetClient::read(uint8_t *buf, size_t bytes) { - return recv(_sock, buf, size, 0); + return recv(_sock, buf, bytes, MSG_DONTWAIT); } int EthernetClient::peek() { uint8_t b; - return recv(_sock, &b, 1, MSG_PEEK | MSG_DONTWAIT); + if (recv(_sock, &b, 1, MSG_PEEK | MSG_DONTWAIT) > 0) { + return b; + } else { + return -1; + } } void EthernetClient::flush() { - // There isn't much we can do here - return; + int count = 0; + + if (_sock != -1) { + while (true) { + ioctl(_sock, SIOCOUTQ, &count); + if (count == 0) { + return; + } + usleep(1000); + } + } } void EthernetClient::stop() @@ -203,7 +216,7 @@ void EthernetClient::stop() // if it hasn't closed, close it forcefully if (s != ETHERNETCLIENT_W5100_CLOSED) { - close(_sock); + ::close(_sock); } _sock = -1; } @@ -249,17 +262,15 @@ uint8_t EthernetClient::status() uint8_t EthernetClient::connected() { - if (_sock == -1) { - return 0; - } + return status() == ETHERNETCLIENT_W5100_ESTABLISHED || available(); +} - if (peek() < 0) { - if (errno == EAGAIN) { - return 1; - } - return 0; +void EthernetClient::close() +{ + if (_sock != -1) { + ::close(_sock); + _sock = -1; } - return 1; } int EthernetClient::getSocketNumber() diff --git a/drivers/Linux/EthernetClient.h b/drivers/Linux/EthernetClient.h index df0b7e8..8562700 100644 --- a/drivers/Linux/EthernetClient.h +++ b/drivers/Linux/EthernetClient.h @@ -57,7 +57,7 @@ public: /** * @brief Initiate a connection with host:port. * - * @param host host name to resolve or a stringified dotted IP address. + * @param host name to resolve or a stringified dotted IP address. * @param port to connect to. * @return 1 if SUCCESS or -1 if FAILURE. */ @@ -101,7 +101,7 @@ public: */ size_t write(const char *buffer, size_t size); /** - * @brief Checks if new data is available. + * @brief Returns the number of bytes available for reading. * * @return number of bytes available. */ @@ -109,27 +109,25 @@ public: /** * @brief Read a byte. * - * @return 0 if FAILURE or 1 if SUCCESS. - * @note This function will block (until data becomes available or timeout is reached). + * @return -1 if no data, else the first byte available. */ virtual int read(); /** * @brief Read a number of bytes and store in a buffer. * - * @return -1 if FAILURE or number of read bytes. - * @note This function will block (until data becomes available or timeout is reached). + * @param buf buffer to write to. + * @param bytes number of bytes to read. + * @return -1 if no data or number of read bytes. */ - virtual int read(uint8_t *buf, size_t size); + virtual int read(uint8_t *buf, size_t bytes); /** - * @brief Check if new data are available. + * @brief Returns the next byte of the read queue without removing it from the queue. * - * @return 0 if no data, else number of bytes available. + * @return -1 if no data, else the first byte of incoming data available. */ virtual int peek(); /** - * @brief Flushes the network buffers. - * - * Empty function due to the way TCP works. + * @brief Waits until all outgoing bytes in buffer have been sent. */ virtual void flush(); /** @@ -145,11 +143,18 @@ public: */ uint8_t status(); /** - * @brief Checks whether the socket is alive. + * @brief Whether or not the client is connected. * - * @return 0 if disconnected or 1 if connected. + * Note that a client is considered connected if the connection has been closed but + * there is still unread data. + * + * @return 1 if the client is connected, 0 if not. */ virtual uint8_t connected(); + /** + * @brief Close the connection. + */ + void close(); /** * @brief Get the internal socket file descriptor. * diff --git a/drivers/Linux/EthernetServer.cpp b/drivers/Linux/EthernetServer.cpp index 79aaccb..d327834 100644 --- a/drivers/Linux/EthernetServer.cpp +++ b/drivers/Linux/EthernetServer.cpp @@ -19,6 +19,7 @@ * Based on Arduino ethernet library, Copyright (c) 2010 Arduino LLC. All right reserved. */ +#include "EthernetServer.h" #include #include #include @@ -30,7 +31,6 @@ #include #include "log.h" #include "EthernetClient.h" -#include "EthernetServer.h" EthernetServer::EthernetServer(uint16_t port, uint16_t max_clients) : port(port), max_clients(max_clients), sockfd(-1) @@ -108,6 +108,24 @@ void EthernetServer::begin(IPAddress address) bool EthernetServer::hasClient() { + // Check if any client has disconnected + for (size_t i = 0; i < clients.size(); ++i) { + EthernetClient client(clients[i]); + if (!client.connected()) { + // Checks if this disconnected client is also on the new clients list + for (std::list::iterator it = new_clients.begin(); it != new_clients.end(); ++it) { + if (*it == clients[i]) { + new_clients.erase(it); + break; + } + } + client.stop(); + clients[i] = clients.back(); + clients.pop_back(); + logDebug("Ethernet client disconnected.\n"); + } + } + _accept(); return !new_clients.empty(); @@ -136,14 +154,9 @@ size_t EthernetServer::write(const uint8_t *buffer, size_t size) while (i < clients.size()) { EthernetClient client(clients[i]); - if (client.connected()) { + if (client.status() == ETHERNETCLIENT_W5100_ESTABLISHED) { n += client.write(buffer, size); i++; - } else if (!client.available()) { - client.stop(); - clients[i] = clients.back(); - clients.pop_back(); - logDebug("Client disconnected.\n"); } } @@ -170,36 +183,6 @@ void EthernetServer::_accept() struct sockaddr_storage client_addr; char ipstr[INET_ADDRSTRLEN]; - if (new_clients.size() + clients.size() == max_clients) { - // no free slots, search for a dead client - bool no_free_slots = true; - for (size_t i = 0; i < clients.size();) { - EthernetClient client(clients[i]); - if (client.connected() || client.available()) { - i++; - } else { - clients[i] = clients.back(); - clients.pop_back(); - no_free_slots = false; - break; - } - } - if (no_free_slots) { - for (std::list::iterator it = new_clients.begin(); it != new_clients.end(); ++it) { - EthernetClient client(*it); - if (!client.connected() && !client.available()) { - new_clients.erase(it); - no_free_slots = false; - break; - } - } - } - if (no_free_slots) { - logDebug("Max number of ethernet clients reached.\n"); - return; - } - } - sin_size = sizeof client_addr; new_fd = accept(sockfd, (struct sockaddr *)&client_addr, &sin_size); if (new_fd == -1) { @@ -209,6 +192,13 @@ void EthernetServer::_accept() return; } + if (clients.size() == max_clients) { + // no free slots, search for a dead client + close(new_fd); + logDebug("Max number of ethernet clients reached.\n"); + return; + } + new_clients.push_back(new_fd); clients.push_back(new_fd); diff --git a/drivers/Linux/EthernetServer.h b/drivers/Linux/EthernetServer.h index 2719cb6..8e5c240 100644 --- a/drivers/Linux/EthernetServer.h +++ b/drivers/Linux/EthernetServer.h @@ -70,7 +70,7 @@ public: /** * @brief Get the new connected client. * - * @return client class object if a new client has connected else -1. + * @return a EthernetClient object; if no new client has connected, this object will evaluate to false. */ EthernetClient available(); /** @@ -106,8 +106,8 @@ public: private: uint16_t port; //!< @brief Port number for the network socket. - std::list new_clients; //!< Socket list of new clients. - std::vector clients; //!< @brief Socket list of clients. + std::list new_clients; //!< Socket list of new connected clients. + std::vector clients; //!< @brief Socket list of connected clients. uint16_t max_clients; //!< @brief The maximum number of allowed clients. int sockfd; //!< @brief Network socket used to accept connections.