2016-10-06 14:21:30 +03:00
|
|
|
/*
|
|
|
|
Client.h - Client class for Raspberry Pi
|
|
|
|
Copyright (c) 2016 Hristo Gochkov All right reserved.
|
|
|
|
|
|
|
|
This library is free software; you can redistribute it and/or
|
|
|
|
modify it under the terms of the GNU Lesser General Public
|
|
|
|
License as published by the Free Software Foundation; either
|
|
|
|
version 2.1 of the License, or (at your option) any later version.
|
|
|
|
|
|
|
|
This library is distributed in the hope that it will be useful,
|
|
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
|
|
Lesser General Public License for more details.
|
|
|
|
|
|
|
|
You should have received a copy of the GNU Lesser General Public
|
|
|
|
License along with this library; if not, write to the Free Software
|
|
|
|
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include "WiFiClient.h"
|
2018-06-27 09:01:06 +02:00
|
|
|
#include "WiFi.h"
|
2016-10-06 14:21:30 +03:00
|
|
|
#include <lwip/sockets.h>
|
|
|
|
#include <lwip/netdb.h>
|
|
|
|
#include <errno.h>
|
|
|
|
|
[WiFiClient] Default connection timeout, when no timeout provided (#5487)
## The problem
WiFiClient's connect method variant where no timeout is passed can block esp32 MCU and may then cause watchdog to kick in and reset the device. This behavior is different from that, what is in arduino-esp8266 core.
## Summary
Some cross-esp libraries (working both on esp32 and 8266), like PubSubClient simply call connect method on WiFiClient, to get connected to remote server. However, connect behavior varies betwen esp arduino 8266 and esp arduino 32 cores. This pull request tries introduce same behavior - to make connect method non-blocking on esp32, like it is with 8266 arduino core.
## Proposed solution
Introduce default fixed timeout that can be changed by #define - by default set to 3 seconds.
### Affected components:
WiFiClient
### Affected methods:
```c++
int connect(IPAddress ip, uint16_t port);
int connect(const char *host, uint16_t port);
```
### Impact
May impact projects or libraries using connect method variant without specified timeout, where:
- remote is located far away or
- connection is heavily limited, or
- remote is slow, when it comes to accept the connection
2021-08-02 14:05:44 +02:00
|
|
|
#define WIFI_CLIENT_DEF_CONN_TIMEOUT (3)
|
2017-02-28 17:37:00 -05:00
|
|
|
#define WIFI_CLIENT_MAX_WRITE_RETRY (10)
|
2017-03-20 11:31:55 +02:00
|
|
|
#define WIFI_CLIENT_SELECT_TIMEOUT_US (1000000)
|
2017-03-02 02:49:53 +02:00
|
|
|
#define WIFI_CLIENT_FLUSH_BUFFER_SIZE (1024)
|
2017-02-28 17:37:00 -05:00
|
|
|
|
2016-10-06 14:21:30 +03:00
|
|
|
#undef connect
|
|
|
|
#undef write
|
|
|
|
#undef read
|
|
|
|
|
2018-07-05 22:31:58 +02:00
|
|
|
class WiFiClientRxBuffer {
|
|
|
|
private:
|
|
|
|
size_t _size;
|
|
|
|
uint8_t *_buffer;
|
|
|
|
size_t _pos;
|
|
|
|
size_t _fill;
|
|
|
|
int _fd;
|
|
|
|
bool _failed;
|
|
|
|
|
|
|
|
size_t r_available()
|
|
|
|
{
|
|
|
|
if(_fd < 0){
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
int count;
|
2021-04-05 14:23:58 +03:00
|
|
|
#ifdef ESP_IDF_VERSION_MAJOR
|
|
|
|
int res = lwip_ioctl(_fd, FIONREAD, &count);
|
|
|
|
#else
|
2018-07-05 22:31:58 +02:00
|
|
|
int res = lwip_ioctl_r(_fd, FIONREAD, &count);
|
2021-04-05 14:23:58 +03:00
|
|
|
#endif
|
2018-07-05 22:31:58 +02:00
|
|
|
if(res < 0) {
|
|
|
|
_failed = true;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
return count;
|
|
|
|
}
|
|
|
|
|
|
|
|
size_t fillBuffer()
|
|
|
|
{
|
|
|
|
if(!_buffer){
|
|
|
|
_buffer = (uint8_t *)malloc(_size);
|
2019-01-02 16:44:17 +01:00
|
|
|
if(!_buffer) {
|
|
|
|
log_e("Not enough memory to allocate buffer");
|
|
|
|
_failed = true;
|
|
|
|
return 0;
|
|
|
|
}
|
2018-07-05 22:31:58 +02:00
|
|
|
}
|
|
|
|
if(_fill && _pos == _fill){
|
|
|
|
_fill = 0;
|
|
|
|
_pos = 0;
|
|
|
|
}
|
|
|
|
if(!_buffer || _size <= _fill || !r_available()) {
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
int res = recv(_fd, _buffer + _fill, _size - _fill, MSG_DONTWAIT);
|
2019-01-02 16:44:17 +01:00
|
|
|
if(res < 0) {
|
|
|
|
if(errno != EWOULDBLOCK) {
|
|
|
|
_failed = true;
|
|
|
|
}
|
2018-07-05 22:31:58 +02:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
_fill += res;
|
|
|
|
return res;
|
|
|
|
}
|
|
|
|
|
|
|
|
public:
|
|
|
|
WiFiClientRxBuffer(int fd, size_t size=1436)
|
|
|
|
:_size(size)
|
|
|
|
,_buffer(NULL)
|
|
|
|
,_pos(0)
|
|
|
|
,_fill(0)
|
|
|
|
,_fd(fd)
|
|
|
|
,_failed(false)
|
|
|
|
{
|
|
|
|
//_buffer = (uint8_t *)malloc(_size);
|
|
|
|
}
|
|
|
|
|
|
|
|
~WiFiClientRxBuffer()
|
|
|
|
{
|
|
|
|
free(_buffer);
|
|
|
|
}
|
|
|
|
|
|
|
|
bool failed(){
|
|
|
|
return _failed;
|
|
|
|
}
|
|
|
|
|
|
|
|
int read(uint8_t * dst, size_t len){
|
|
|
|
if(!dst || !len || (_pos == _fill && !fillBuffer())){
|
2020-10-27 03:01:41 -07:00
|
|
|
return _failed ? -1 : 0;
|
2018-07-05 22:31:58 +02:00
|
|
|
}
|
|
|
|
size_t a = _fill - _pos;
|
|
|
|
if(len <= a || ((len - a) <= (_size - _fill) && fillBuffer() >= (len - a))){
|
|
|
|
if(len == 1){
|
|
|
|
*dst = _buffer[_pos];
|
|
|
|
} else {
|
|
|
|
memcpy(dst, _buffer + _pos, len);
|
|
|
|
}
|
|
|
|
_pos += len;
|
|
|
|
return len;
|
|
|
|
}
|
|
|
|
size_t left = len;
|
|
|
|
size_t toRead = a;
|
|
|
|
uint8_t * buf = dst;
|
|
|
|
memcpy(buf, _buffer + _pos, toRead);
|
|
|
|
_pos += toRead;
|
|
|
|
left -= toRead;
|
|
|
|
buf += toRead;
|
|
|
|
while(left){
|
|
|
|
if(!fillBuffer()){
|
|
|
|
return len - left;
|
|
|
|
}
|
|
|
|
a = _fill - _pos;
|
|
|
|
toRead = (a > left)?left:a;
|
|
|
|
memcpy(buf, _buffer + _pos, toRead);
|
|
|
|
_pos += toRead;
|
|
|
|
left -= toRead;
|
|
|
|
buf += toRead;
|
|
|
|
}
|
|
|
|
return len;
|
|
|
|
}
|
|
|
|
|
|
|
|
int peek(){
|
|
|
|
if(_pos == _fill && !fillBuffer()){
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
return _buffer[_pos];
|
|
|
|
}
|
|
|
|
|
|
|
|
size_t available(){
|
|
|
|
return _fill - _pos + r_available();
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2017-03-01 17:47:16 -05:00
|
|
|
class WiFiClientSocketHandle {
|
|
|
|
private:
|
|
|
|
int sockfd;
|
|
|
|
|
|
|
|
public:
|
|
|
|
WiFiClientSocketHandle(int fd):sockfd(fd)
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
~WiFiClientSocketHandle()
|
|
|
|
{
|
|
|
|
close(sockfd);
|
|
|
|
}
|
|
|
|
|
|
|
|
int fd()
|
|
|
|
{
|
|
|
|
return sockfd;
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
WiFiClient::WiFiClient():_connected(false),next(NULL)
|
2016-10-06 14:21:30 +03:00
|
|
|
{
|
|
|
|
}
|
|
|
|
|
2017-03-01 17:47:16 -05:00
|
|
|
WiFiClient::WiFiClient(int fd):_connected(true),next(NULL)
|
2016-10-06 14:21:30 +03:00
|
|
|
{
|
2017-03-01 17:47:16 -05:00
|
|
|
clientSocketHandle.reset(new WiFiClientSocketHandle(fd));
|
2018-07-05 22:31:58 +02:00
|
|
|
_rxBuffer.reset(new WiFiClientRxBuffer(fd));
|
2016-10-06 14:21:30 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
WiFiClient::~WiFiClient()
|
|
|
|
{
|
2016-11-29 03:27:01 +02:00
|
|
|
stop();
|
2016-10-06 14:21:30 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
WiFiClient & WiFiClient::operator=(const WiFiClient &other)
|
|
|
|
{
|
|
|
|
stop();
|
2017-03-01 17:47:16 -05:00
|
|
|
clientSocketHandle = other.clientSocketHandle;
|
2018-07-05 22:31:58 +02:00
|
|
|
_rxBuffer = other._rxBuffer;
|
2016-10-06 14:21:30 +03:00
|
|
|
_connected = other._connected;
|
|
|
|
return *this;
|
|
|
|
}
|
|
|
|
|
|
|
|
void WiFiClient::stop()
|
|
|
|
{
|
2020-01-29 12:30:08 +02:00
|
|
|
clientSocketHandle = NULL;
|
|
|
|
_rxBuffer = NULL;
|
2017-03-01 17:47:16 -05:00
|
|
|
_connected = false;
|
2016-10-06 14:21:30 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
int WiFiClient::connect(IPAddress ip, uint16_t port)
|
2019-02-14 22:55:50 +11:00
|
|
|
{
|
[WiFiClient] Default connection timeout, when no timeout provided (#5487)
## The problem
WiFiClient's connect method variant where no timeout is passed can block esp32 MCU and may then cause watchdog to kick in and reset the device. This behavior is different from that, what is in arduino-esp8266 core.
## Summary
Some cross-esp libraries (working both on esp32 and 8266), like PubSubClient simply call connect method on WiFiClient, to get connected to remote server. However, connect behavior varies betwen esp arduino 8266 and esp arduino 32 cores. This pull request tries introduce same behavior - to make connect method non-blocking on esp32, like it is with 8266 arduino core.
## Proposed solution
Introduce default fixed timeout that can be changed by #define - by default set to 3 seconds.
### Affected components:
WiFiClient
### Affected methods:
```c++
int connect(IPAddress ip, uint16_t port);
int connect(const char *host, uint16_t port);
```
### Impact
May impact projects or libraries using connect method variant without specified timeout, where:
- remote is located far away or
- connection is heavily limited, or
- remote is slow, when it comes to accept the connection
2021-08-02 14:05:44 +02:00
|
|
|
return connect(ip,port,WIFI_CLIENT_DEF_CONN_TIMEOUT);
|
2019-02-14 22:55:50 +11:00
|
|
|
}
|
|
|
|
int WiFiClient::connect(IPAddress ip, uint16_t port, int32_t timeout)
|
2016-10-06 14:21:30 +03:00
|
|
|
{
|
2017-03-01 17:47:16 -05:00
|
|
|
int sockfd = socket(AF_INET, SOCK_STREAM, 0);
|
2016-10-06 14:21:30 +03:00
|
|
|
if (sockfd < 0) {
|
2016-11-13 17:30:21 +02:00
|
|
|
log_e("socket: %d", errno);
|
2016-10-06 14:21:30 +03:00
|
|
|
return 0;
|
|
|
|
}
|
2019-02-14 22:55:50 +11:00
|
|
|
fcntl( sockfd, F_SETFL, fcntl( sockfd, F_GETFL, 0 ) | O_NONBLOCK );
|
2017-03-01 17:47:16 -05:00
|
|
|
|
2016-10-06 14:21:30 +03:00
|
|
|
uint32_t ip_addr = ip;
|
|
|
|
struct sockaddr_in serveraddr;
|
2020-12-01 15:35:12 +01:00
|
|
|
memset((char *) &serveraddr, 0, sizeof(serveraddr));
|
2016-10-06 14:21:30 +03:00
|
|
|
serveraddr.sin_family = AF_INET;
|
2020-12-01 15:35:12 +01:00
|
|
|
memcpy((void *)&serveraddr.sin_addr.s_addr, (const void *)(&ip_addr), 4);
|
2016-10-06 14:21:30 +03:00
|
|
|
serveraddr.sin_port = htons(port);
|
2019-02-14 22:55:50 +11:00
|
|
|
fd_set fdset;
|
|
|
|
struct timeval tv;
|
|
|
|
FD_ZERO(&fdset);
|
|
|
|
FD_SET(sockfd, &fdset);
|
|
|
|
tv.tv_sec = 0;
|
|
|
|
tv.tv_usec = timeout * 1000;
|
2019-07-09 18:48:05 +02:00
|
|
|
|
2021-04-05 14:23:58 +03:00
|
|
|
#ifdef ESP_IDF_VERSION_MAJOR
|
|
|
|
int res = lwip_connect(sockfd, (struct sockaddr*)&serveraddr, sizeof(serveraddr));
|
|
|
|
#else
|
2019-07-09 18:48:05 +02:00
|
|
|
int res = lwip_connect_r(sockfd, (struct sockaddr*)&serveraddr, sizeof(serveraddr));
|
2021-04-05 14:23:58 +03:00
|
|
|
#endif
|
2019-07-09 18:48:05 +02:00
|
|
|
if (res < 0 && errno != EINPROGRESS) {
|
|
|
|
log_e("connect on fd %d, errno: %d, \"%s\"", sockfd, errno, strerror(errno));
|
2016-10-06 14:21:30 +03:00
|
|
|
close(sockfd);
|
|
|
|
return 0;
|
|
|
|
}
|
2019-07-09 18:48:05 +02:00
|
|
|
|
|
|
|
res = select(sockfd + 1, nullptr, &fdset, nullptr, timeout<0 ? nullptr : &tv);
|
|
|
|
if (res < 0) {
|
|
|
|
log_e("select on fd %d, errno: %d, \"%s\"", sockfd, errno, strerror(errno));
|
|
|
|
close(sockfd);
|
|
|
|
return 0;
|
|
|
|
} else if (res == 0) {
|
|
|
|
log_i("select returned due to timeout %d ms for fd %d", timeout, sockfd);
|
|
|
|
close(sockfd);
|
|
|
|
return 0;
|
|
|
|
} else {
|
|
|
|
int sockerr;
|
|
|
|
socklen_t len = (socklen_t)sizeof(int);
|
|
|
|
res = getsockopt(sockfd, SOL_SOCKET, SO_ERROR, &sockerr, &len);
|
|
|
|
|
|
|
|
if (res < 0) {
|
|
|
|
log_e("getsockopt on fd %d, errno: %d, \"%s\"", sockfd, errno, strerror(errno));
|
|
|
|
close(sockfd);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (sockerr != 0) {
|
|
|
|
log_e("socket error on fd %d, errno: %d, \"%s\"", sockfd, sockerr, strerror(sockerr));
|
|
|
|
close(sockfd);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-02-14 22:55:50 +11:00
|
|
|
fcntl( sockfd, F_SETFL, fcntl( sockfd, F_GETFL, 0 ) & (~O_NONBLOCK) );
|
2017-03-01 17:47:16 -05:00
|
|
|
clientSocketHandle.reset(new WiFiClientSocketHandle(sockfd));
|
2018-07-05 22:31:58 +02:00
|
|
|
_rxBuffer.reset(new WiFiClientRxBuffer(sockfd));
|
2016-10-06 14:21:30 +03:00
|
|
|
_connected = true;
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
int WiFiClient::connect(const char *host, uint16_t port)
|
2019-02-14 22:55:50 +11:00
|
|
|
{
|
[WiFiClient] Default connection timeout, when no timeout provided (#5487)
## The problem
WiFiClient's connect method variant where no timeout is passed can block esp32 MCU and may then cause watchdog to kick in and reset the device. This behavior is different from that, what is in arduino-esp8266 core.
## Summary
Some cross-esp libraries (working both on esp32 and 8266), like PubSubClient simply call connect method on WiFiClient, to get connected to remote server. However, connect behavior varies betwen esp arduino 8266 and esp arduino 32 cores. This pull request tries introduce same behavior - to make connect method non-blocking on esp32, like it is with 8266 arduino core.
## Proposed solution
Introduce default fixed timeout that can be changed by #define - by default set to 3 seconds.
### Affected components:
WiFiClient
### Affected methods:
```c++
int connect(IPAddress ip, uint16_t port);
int connect(const char *host, uint16_t port);
```
### Impact
May impact projects or libraries using connect method variant without specified timeout, where:
- remote is located far away or
- connection is heavily limited, or
- remote is slow, when it comes to accept the connection
2021-08-02 14:05:44 +02:00
|
|
|
return connect(host,port,WIFI_CLIENT_DEF_CONN_TIMEOUT);
|
2019-02-14 22:55:50 +11:00
|
|
|
}
|
|
|
|
int WiFiClient::connect(const char *host, uint16_t port, int32_t timeout)
|
2016-10-06 14:21:30 +03:00
|
|
|
{
|
2018-06-27 09:01:06 +02:00
|
|
|
IPAddress srv((uint32_t)0);
|
|
|
|
if(!WiFiGenericClass::hostByName(host, srv)){
|
2016-10-06 14:21:30 +03:00
|
|
|
return 0;
|
|
|
|
}
|
2019-02-14 22:55:50 +11:00
|
|
|
return connect(srv, port, timeout);
|
2016-10-06 14:21:30 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
int WiFiClient::setSocketOption(int option, char* value, size_t len)
|
|
|
|
{
|
2017-03-01 17:47:16 -05:00
|
|
|
int res = setsockopt(fd(), SOL_SOCKET, option, value, len);
|
2016-10-06 14:21:30 +03:00
|
|
|
if(res < 0) {
|
2017-09-29 20:31:45 +08:00
|
|
|
log_e("%X : %d", option, errno);
|
2016-10-06 14:21:30 +03:00
|
|
|
}
|
|
|
|
return res;
|
|
|
|
}
|
|
|
|
|
|
|
|
int WiFiClient::setTimeout(uint32_t seconds)
|
|
|
|
{
|
2018-11-19 16:57:23 +01:00
|
|
|
Client::setTimeout(seconds * 1000);
|
2016-10-06 14:21:30 +03:00
|
|
|
struct timeval tv;
|
|
|
|
tv.tv_sec = seconds;
|
|
|
|
tv.tv_usec = 0;
|
|
|
|
if(setSocketOption(SO_RCVTIMEO, (char *)&tv, sizeof(struct timeval)) < 0) {
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
return setSocketOption(SO_SNDTIMEO, (char *)&tv, sizeof(struct timeval));
|
|
|
|
}
|
|
|
|
|
|
|
|
int WiFiClient::setOption(int option, int *value)
|
|
|
|
{
|
2017-03-01 17:47:16 -05:00
|
|
|
int res = setsockopt(fd(), IPPROTO_TCP, option, (char *) value, sizeof(int));
|
2016-10-06 14:21:30 +03:00
|
|
|
if(res < 0) {
|
2019-04-12 09:44:16 -04:00
|
|
|
log_e("fail on fd %d, errno: %d, \"%s\"", fd(), errno, strerror(errno));
|
2016-10-06 14:21:30 +03:00
|
|
|
}
|
|
|
|
return res;
|
|
|
|
}
|
|
|
|
|
|
|
|
int WiFiClient::getOption(int option, int *value)
|
|
|
|
{
|
2021-04-14 18:10:05 +03:00
|
|
|
socklen_t size = sizeof(int);
|
2017-03-01 17:47:16 -05:00
|
|
|
int res = getsockopt(fd(), IPPROTO_TCP, option, (char *)value, &size);
|
2016-10-06 14:21:30 +03:00
|
|
|
if(res < 0) {
|
2019-04-12 09:44:16 -04:00
|
|
|
log_e("fail on fd %d, errno: %d, \"%s\"", fd(), errno, strerror(errno));
|
2016-10-06 14:21:30 +03:00
|
|
|
}
|
|
|
|
return res;
|
|
|
|
}
|
|
|
|
|
|
|
|
int WiFiClient::setNoDelay(bool nodelay)
|
|
|
|
{
|
|
|
|
int flag = nodelay;
|
|
|
|
return setOption(TCP_NODELAY, &flag);
|
|
|
|
}
|
|
|
|
|
|
|
|
bool WiFiClient::getNoDelay()
|
|
|
|
{
|
|
|
|
int flag = 0;
|
|
|
|
getOption(TCP_NODELAY, &flag);
|
|
|
|
return flag;
|
|
|
|
}
|
|
|
|
|
|
|
|
size_t WiFiClient::write(uint8_t data)
|
|
|
|
{
|
|
|
|
return write(&data, 1);
|
|
|
|
}
|
|
|
|
|
|
|
|
int WiFiClient::read()
|
|
|
|
{
|
|
|
|
uint8_t data = 0;
|
|
|
|
int res = read(&data, 1);
|
|
|
|
if(res < 0) {
|
|
|
|
return res;
|
|
|
|
}
|
2020-10-27 03:01:41 -07:00
|
|
|
if (res == 0) { // No data available.
|
|
|
|
return -1;
|
|
|
|
}
|
2016-10-06 14:21:30 +03:00
|
|
|
return data;
|
|
|
|
}
|
|
|
|
|
|
|
|
size_t WiFiClient::write(const uint8_t *buf, size_t size)
|
|
|
|
{
|
2017-02-28 17:37:00 -05:00
|
|
|
int res =0;
|
|
|
|
int retry = WIFI_CLIENT_MAX_WRITE_RETRY;
|
|
|
|
int socketFileDescriptor = fd();
|
2017-07-31 09:51:41 -10:00
|
|
|
size_t totalBytesSent = 0;
|
|
|
|
size_t bytesRemaining = size;
|
2017-02-28 17:37:00 -05:00
|
|
|
|
|
|
|
if(!_connected || (socketFileDescriptor < 0)) {
|
2016-10-06 14:21:30 +03:00
|
|
|
return 0;
|
|
|
|
}
|
2017-02-28 17:37:00 -05:00
|
|
|
|
|
|
|
while(retry) {
|
|
|
|
//use select to make sure the socket is ready for writing
|
|
|
|
fd_set set;
|
|
|
|
struct timeval tv;
|
|
|
|
FD_ZERO(&set); // empties the set
|
|
|
|
FD_SET(socketFileDescriptor, &set); // adds FD to the set
|
|
|
|
tv.tv_sec = 0;
|
|
|
|
tv.tv_usec = WIFI_CLIENT_SELECT_TIMEOUT_US;
|
|
|
|
retry--;
|
|
|
|
|
|
|
|
if(select(socketFileDescriptor + 1, NULL, &set, NULL, &tv) < 0) {
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
if(FD_ISSET(socketFileDescriptor, &set)) {
|
2017-07-31 09:51:41 -10:00
|
|
|
res = send(socketFileDescriptor, (void*) buf, bytesRemaining, MSG_DONTWAIT);
|
|
|
|
if(res > 0) {
|
|
|
|
totalBytesSent += res;
|
|
|
|
if (totalBytesSent >= size) {
|
|
|
|
//completed successfully
|
|
|
|
retry = 0;
|
|
|
|
} else {
|
|
|
|
buf += res;
|
|
|
|
bytesRemaining -= res;
|
2019-04-10 03:41:29 +03:00
|
|
|
retry = WIFI_CLIENT_MAX_WRITE_RETRY;
|
2017-07-31 09:51:41 -10:00
|
|
|
}
|
|
|
|
}
|
|
|
|
else if(res < 0) {
|
2019-04-12 09:44:16 -04:00
|
|
|
log_e("fail on fd %d, errno: %d, \"%s\"", fd(), errno, strerror(errno));
|
2017-02-28 17:37:00 -05:00
|
|
|
if(errno != EAGAIN) {
|
|
|
|
//if resource was busy, can try again, otherwise give up
|
|
|
|
stop();
|
|
|
|
res = 0;
|
|
|
|
retry = 0;
|
|
|
|
}
|
2017-07-31 09:51:41 -10:00
|
|
|
}
|
|
|
|
else {
|
|
|
|
// Try again
|
2017-02-28 17:37:00 -05:00
|
|
|
}
|
|
|
|
}
|
2016-10-06 14:21:30 +03:00
|
|
|
}
|
2017-07-31 09:51:41 -10:00
|
|
|
return totalBytesSent;
|
2016-10-06 14:21:30 +03:00
|
|
|
}
|
|
|
|
|
2017-06-14 01:22:16 -07:00
|
|
|
size_t WiFiClient::write_P(PGM_P buf, size_t size)
|
|
|
|
{
|
|
|
|
return write(buf, size);
|
|
|
|
}
|
|
|
|
|
2018-06-27 09:01:06 +02:00
|
|
|
size_t WiFiClient::write(Stream &stream)
|
|
|
|
{
|
|
|
|
uint8_t * buf = (uint8_t *)malloc(1360);
|
|
|
|
if(!buf){
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
size_t toRead = 0, toWrite = 0, written = 0;
|
|
|
|
size_t available = stream.available();
|
|
|
|
while(available){
|
|
|
|
toRead = (available > 1360)?1360:available;
|
|
|
|
toWrite = stream.readBytes(buf, toRead);
|
|
|
|
written += write(buf, toWrite);
|
|
|
|
available = stream.available();
|
|
|
|
}
|
|
|
|
free(buf);
|
|
|
|
return written;
|
|
|
|
}
|
|
|
|
|
2016-10-06 14:21:30 +03:00
|
|
|
int WiFiClient::read(uint8_t *buf, size_t size)
|
|
|
|
{
|
2018-07-05 22:31:58 +02:00
|
|
|
int res = -1;
|
2021-06-09 11:30:14 +02:00
|
|
|
if (_rxBuffer) {
|
|
|
|
res = _rxBuffer->read(buf, size);
|
|
|
|
if(_rxBuffer->failed()) {
|
|
|
|
log_e("fail on fd %d, errno: %d, \"%s\"", fd(), errno, strerror(errno));
|
|
|
|
stop();
|
|
|
|
}
|
2016-10-06 14:21:30 +03:00
|
|
|
}
|
|
|
|
return res;
|
|
|
|
}
|
|
|
|
|
2017-05-22 15:27:34 +03:00
|
|
|
int WiFiClient::peek()
|
|
|
|
{
|
2021-06-09 11:30:14 +02:00
|
|
|
int res = -1;
|
|
|
|
if (_rxBuffer) {
|
|
|
|
res = _rxBuffer->peek();
|
|
|
|
if(_rxBuffer->failed()) {
|
|
|
|
log_e("fail on fd %d, errno: %d, \"%s\"", fd(), errno, strerror(errno));
|
|
|
|
stop();
|
|
|
|
}
|
2017-05-22 15:27:34 +03:00
|
|
|
}
|
2018-07-05 22:31:58 +02:00
|
|
|
return res;
|
2017-05-22 15:27:34 +03:00
|
|
|
}
|
|
|
|
|
2016-10-06 14:21:30 +03:00
|
|
|
int WiFiClient::available()
|
|
|
|
{
|
2018-12-06 19:01:06 +01:00
|
|
|
if(!_rxBuffer)
|
|
|
|
{
|
|
|
|
return 0;
|
|
|
|
}
|
2018-07-05 22:31:58 +02:00
|
|
|
int res = _rxBuffer->available();
|
|
|
|
if(_rxBuffer->failed()) {
|
2019-04-12 09:44:16 -04:00
|
|
|
log_e("fail on fd %d, errno: %d, \"%s\"", fd(), errno, strerror(errno));
|
2016-12-01 20:27:51 +09:00
|
|
|
stop();
|
2016-10-06 14:21:30 +03:00
|
|
|
}
|
2018-07-05 22:31:58 +02:00
|
|
|
return res;
|
2016-10-06 14:21:30 +03:00
|
|
|
}
|
|
|
|
|
2017-03-02 02:49:53 +02:00
|
|
|
// Though flushing means to send all pending data,
|
|
|
|
// seems that in Arduino it also means to clear RX
|
|
|
|
void WiFiClient::flush() {
|
2017-03-03 14:56:18 +02:00
|
|
|
int res;
|
2017-03-02 02:49:53 +02:00
|
|
|
size_t a = available(), toRead = 0;
|
|
|
|
if(!a){
|
|
|
|
return;//nothing to flush
|
|
|
|
}
|
|
|
|
uint8_t * buf = (uint8_t *)malloc(WIFI_CLIENT_FLUSH_BUFFER_SIZE);
|
|
|
|
if(!buf){
|
|
|
|
return;//memory error
|
|
|
|
}
|
|
|
|
while(a){
|
|
|
|
toRead = (a>WIFI_CLIENT_FLUSH_BUFFER_SIZE)?WIFI_CLIENT_FLUSH_BUFFER_SIZE:a;
|
2017-03-03 14:56:18 +02:00
|
|
|
res = recv(fd(), buf, toRead, MSG_DONTWAIT);
|
|
|
|
if(res < 0) {
|
2019-04-12 09:44:16 -04:00
|
|
|
log_e("fail on fd %d, errno: %d, \"%s\"", fd(), errno, strerror(errno));
|
2017-03-02 12:32:32 +02:00
|
|
|
stop();
|
|
|
|
break;
|
2017-03-02 02:49:53 +02:00
|
|
|
}
|
2017-03-03 14:56:18 +02:00
|
|
|
a -= res;
|
2017-03-02 02:49:53 +02:00
|
|
|
}
|
|
|
|
free(buf);
|
|
|
|
}
|
|
|
|
|
2016-10-06 14:21:30 +03:00
|
|
|
uint8_t WiFiClient::connected()
|
|
|
|
{
|
2017-05-22 02:16:07 -10:00
|
|
|
if (_connected) {
|
|
|
|
uint8_t dummy;
|
|
|
|
int res = recv(fd(), &dummy, 0, MSG_DONTWAIT);
|
2018-12-03 16:17:16 +01:00
|
|
|
// avoid unused var warning by gcc
|
|
|
|
(void)res;
|
2020-01-21 05:36:03 -05:00
|
|
|
// recv only sets errno if res is <= 0
|
|
|
|
if (res <= 0){
|
2020-01-20 09:08:17 -05:00
|
|
|
switch (errno) {
|
|
|
|
case EWOULDBLOCK:
|
|
|
|
case ENOENT: //caused by vfs
|
|
|
|
_connected = true;
|
|
|
|
break;
|
|
|
|
case ENOTCONN:
|
|
|
|
case EPIPE:
|
|
|
|
case ECONNRESET:
|
|
|
|
case ECONNREFUSED:
|
|
|
|
case ECONNABORTED:
|
|
|
|
_connected = false;
|
|
|
|
log_d("Disconnected: RES: %d, ERR: %d", res, errno);
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
log_i("Unexpected: RES: %d, ERR: %d", res, errno);
|
|
|
|
_connected = true;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
_connected = true;
|
2017-05-22 02:16:07 -10:00
|
|
|
}
|
|
|
|
}
|
2016-10-06 14:21:30 +03:00
|
|
|
return _connected;
|
|
|
|
}
|
|
|
|
|
2017-03-01 17:47:16 -05:00
|
|
|
IPAddress WiFiClient::remoteIP(int fd) const
|
2016-10-06 14:21:30 +03:00
|
|
|
{
|
|
|
|
struct sockaddr_storage addr;
|
|
|
|
socklen_t len = sizeof addr;
|
|
|
|
getpeername(fd, (struct sockaddr*)&addr, &len);
|
|
|
|
struct sockaddr_in *s = (struct sockaddr_in *)&addr;
|
|
|
|
return IPAddress((uint32_t)(s->sin_addr.s_addr));
|
|
|
|
}
|
|
|
|
|
2017-03-01 17:47:16 -05:00
|
|
|
uint16_t WiFiClient::remotePort(int fd) const
|
2016-10-06 14:21:30 +03:00
|
|
|
{
|
|
|
|
struct sockaddr_storage addr;
|
|
|
|
socklen_t len = sizeof addr;
|
|
|
|
getpeername(fd, (struct sockaddr*)&addr, &len);
|
|
|
|
struct sockaddr_in *s = (struct sockaddr_in *)&addr;
|
|
|
|
return ntohs(s->sin_port);
|
|
|
|
}
|
|
|
|
|
2017-03-01 17:47:16 -05:00
|
|
|
IPAddress WiFiClient::remoteIP() const
|
2016-10-06 14:21:30 +03:00
|
|
|
{
|
2017-03-01 17:47:16 -05:00
|
|
|
return remoteIP(fd());
|
2016-10-06 14:21:30 +03:00
|
|
|
}
|
|
|
|
|
2017-03-01 17:47:16 -05:00
|
|
|
uint16_t WiFiClient::remotePort() const
|
2016-10-06 14:21:30 +03:00
|
|
|
{
|
2017-03-01 17:47:16 -05:00
|
|
|
return remotePort(fd());
|
2016-10-06 14:21:30 +03:00
|
|
|
}
|
|
|
|
|
2017-06-14 01:17:14 -07:00
|
|
|
IPAddress WiFiClient::localIP(int fd) const
|
|
|
|
{
|
|
|
|
struct sockaddr_storage addr;
|
|
|
|
socklen_t len = sizeof addr;
|
|
|
|
getsockname(fd, (struct sockaddr*)&addr, &len);
|
|
|
|
struct sockaddr_in *s = (struct sockaddr_in *)&addr;
|
|
|
|
return IPAddress((uint32_t)(s->sin_addr.s_addr));
|
|
|
|
}
|
|
|
|
|
|
|
|
uint16_t WiFiClient::localPort(int fd) const
|
|
|
|
{
|
|
|
|
struct sockaddr_storage addr;
|
|
|
|
socklen_t len = sizeof addr;
|
|
|
|
getsockname(fd, (struct sockaddr*)&addr, &len);
|
|
|
|
struct sockaddr_in *s = (struct sockaddr_in *)&addr;
|
|
|
|
return ntohs(s->sin_port);
|
|
|
|
}
|
|
|
|
|
|
|
|
IPAddress WiFiClient::localIP() const
|
|
|
|
{
|
|
|
|
return localIP(fd());
|
|
|
|
}
|
|
|
|
|
|
|
|
uint16_t WiFiClient::localPort() const
|
|
|
|
{
|
|
|
|
return localPort(fd());
|
|
|
|
}
|
|
|
|
|
2016-10-06 14:21:30 +03:00
|
|
|
bool WiFiClient::operator==(const WiFiClient& rhs)
|
|
|
|
{
|
2017-03-01 17:47:16 -05:00
|
|
|
return clientSocketHandle == rhs.clientSocketHandle && remotePort() == rhs.remotePort() && remoteIP() == rhs.remoteIP();
|
|
|
|
}
|
|
|
|
|
|
|
|
int WiFiClient::fd() const
|
|
|
|
{
|
|
|
|
if (clientSocketHandle == NULL) {
|
|
|
|
return -1;
|
|
|
|
} else {
|
|
|
|
return clientSocketHandle->fd();
|
|
|
|
}
|
2016-10-06 14:21:30 +03:00
|
|
|
}
|
2017-03-01 17:47:16 -05:00
|
|
|
|