PPPoSclient/src/PPPOSClient.cpp
2021-08-24 11:08:46 +03:00

269 lines
7 KiB
C++

#include "PPPOSClient.h"
void PPPOSClient::stop()
{
lwip_close(_socket);
_startPos = 0;
_endPos = 0;
bzero(RxBuffer, sizeof(RxBuffer));
_connected = false;
}
int PPPOSClient::connect(IPAddress ip, uint16_t port)
{
int32_t timeout = PPPOS_CLIENT_DEF_CONN_TIMEOUT_MS;
int sockfd = socket(AF_INET, SOCK_STREAM, 0);
if (sockfd < 0) {
log_e("socket: %d", errno);
return 0;
}
fcntl( sockfd, F_SETFL, fcntl( sockfd, F_GETFL, 0 ) | O_NONBLOCK );
uint32_t ip_addr = ip;
struct sockaddr_in serveraddr;
memset((char *) &serveraddr, 0, sizeof(serveraddr));
serveraddr.sin_family = AF_INET;
memcpy((void *)&serveraddr.sin_addr.s_addr, (const void *)(&ip_addr), 4);
serveraddr.sin_port = htons(port);
fd_set fdset;
struct timeval tv;
FD_ZERO(&fdset);
FD_SET(sockfd, &fdset);
tv.tv_sec = 0;
tv.tv_usec = timeout * 1000;
#ifdef ESP_IDF_VERSION_MAJOR
int res = lwip_connect(sockfd, (struct sockaddr*)&serveraddr, sizeof(serveraddr));
#else
int res = lwip_connect_r(sockfd, (struct sockaddr*)&serveraddr, sizeof(serveraddr));
#endif
if (res < 0 && errno != EINPROGRESS) {
log_e("connect on fd %d, errno: %d, \"%s\"", sockfd, errno, strerror(errno));
close(sockfd);
return 0;
}
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;
}
}
fcntl( sockfd, F_SETFL, fcntl( sockfd, F_GETFL, 0 ) & (~O_NONBLOCK) );
_connected = true;
_socket = sockfd;
return 1;
}
int PPPOSClient::connect(const char *host, uint16_t port)
{
ip_addr_t ip_addr;
IPAddress aResult = static_cast<uint32_t>(0);
struct in_addr retAddr;
struct hostent* he = gethostbyname(host);
if (he == nullptr) {
retAddr.s_addr = 0;
return 0;
} else {
retAddr = *(struct in_addr*) (he->h_addr_list[0]);
}
inet_aton(inet_ntoa(retAddr), &ip_addr);
aResult = ip_addr.u_addr.ip4.addr;
return connect(aResult, port);
}
size_t PPPOSClient::write(uint8_t data)
{
return write(&data, 1);
}
int PPPOSClient::read()
{
if (!_connected) return -1;
if (_startPos >= (_endPos - 1)) {
_startPos = 0;
_endPos = 0;
bzero(RxBuffer, sizeof(RxBuffer));
int _r = lwip_recv(_socket, RxBuffer, sizeof(RxBuffer)-1, MSG_DONTWAIT);
if (_r > 0) {
_endPos = _r + 1;
} else {
return -1;
}
}
_startPos++;
return RxBuffer[_startPos-1];
}
size_t PPPOSClient::write(const uint8_t *buf, size_t size)
{
int res =0;
int retry = PPPOS_CLIENT_MAX_WRITE_RETRY;
int socketFileDescriptor = _socket;
size_t totalBytesSent = 0;
size_t bytesRemaining = size;
if(!_connected || (socketFileDescriptor < 0)) {
return 0;
}
while(retry) {
fd_set set;
struct timeval tv;
FD_ZERO(&set);
FD_SET(socketFileDescriptor, &set);
tv.tv_sec = 0;
tv.tv_usec = PPPOS_CLIENT_SELECT_TIMEOUT_US;
retry--;
if(select(socketFileDescriptor + 1, NULL, &set, NULL, &tv) < 0) {
return 0;
}
if(FD_ISSET(socketFileDescriptor, &set)) {
res = lwip_send(socketFileDescriptor, (void*) buf, bytesRemaining, MSG_DONTWAIT);
if(res > 0) {
totalBytesSent += res;
if (totalBytesSent >= size) {
retry = 0;
} else {
buf += res;
bytesRemaining -= res;
retry = PPPOS_CLIENT_MAX_WRITE_RETRY;
}
}
else if(res < 0) {
log_e("fail on fd %d, errno: %d, \"%s\"", fd(), errno, strerror(errno));
if(errno != EAGAIN) {
stop();
res = 0;
retry = 0;
}
}
else {
}
}
}
return totalBytesSent;
}
int PPPOSClient::available()
{
if (!_connected) return false;
if (_startPos >= (_endPos - 1)) {
_startPos = 0;
_endPos = 0;
bzero(RxBuffer, sizeof(RxBuffer));
int _r = lwip_recv(_socket, RxBuffer, sizeof(RxBuffer)-1, MSG_DONTWAIT);
if (_r > 0) {
_endPos = _r + 1;
} else {
return false;
}
}
return true;
}
void PPPOSClient::flush() {
char recv_buf[5000];
int r = 0;
do {
bzero(recv_buf, sizeof(recv_buf));
r = lwip_recv(_socket, recv_buf, sizeof(recv_buf)-1, MSG_DONTWAIT);
}while(r > 0);
_startPos = 0;
_endPos = 0;
bzero(RxBuffer, sizeof(RxBuffer));
}
uint8_t PPPOSClient::connected() {
if (_connected) {
uint8_t dummy;
int res = lwip_recv(_socket, &dummy, 0, MSG_DONTWAIT);
(void)res;
if (res <= 0){
switch (errno) {
case EWOULDBLOCK:
case ENOENT:
_connected = true;
break;
case ENOTCONN:
case EPIPE:
case ECONNRESET:
case ECONNREFUSED:
case ECONNABORTED:
_connected = false;
log_e("Disconnected: RES: %d, ERR: %d", res, errno);
break;
default:
log_e("Unexpected: RES: %d, ERR: %d", res, errno);
_connected = true;
break;
}
} else {
_connected = true;
}
}
return _connected;
}
int PPPOSClient::read(uint8_t *buf, size_t size) {
if (!_connected) return -1;
int res = -1;
int j = 0;
if (available()){
for (int i = _startPos; i < (_endPos - 1); i++) {
if (j < size) {
buf[j] = RxBuffer[i];
} else {
break;
}
j++;
}
}
if (j > 0) {
_startPos += j;
res = j;
}
return res;
}
int PPPOSClient::peek() {
if (!_connected) return -1;
if (_startPos >= (_endPos - 1)) {
_startPos = 0;
_endPos = 0;
bzero(RxBuffer, sizeof(RxBuffer));
int _r = lwip_recv(_socket, RxBuffer, sizeof(RxBuffer)-1, MSG_DONTWAIT);
if (_r > 0) {
_endPos = _r + 1;
} else {
_connected = false;
return -1;
}
}
return RxBuffer[_startPos];
}