get rid of wifi client, directly use the socket api

This commit is contained in:
wellenvogel 2021-12-28 18:08:06 +01:00
parent fb435499a1
commit 61e0e66acb
2 changed files with 397 additions and 191 deletions

View File

@ -3,188 +3,379 @@
#include <lwip/sockets.h> #include <lwip/sockets.h>
#include "GwBuffer.h" #include "GwBuffer.h"
class GwClient{ class GwClient
public: {
wiFiClientPtr client; public:
int overflows; int fd=-1;
String remoteIp; int overflows;
private: String remoteIpAddress;
unsigned long lastWrite=0;
unsigned long writeTimeout=10000; private:
bool pendingWrite=false; unsigned long lastWrite = 0;
bool writeError=false; unsigned long writeTimeout = 10000;
bool allowRead; bool pendingWrite = false;
GwBuffer *buffer=NULL; bool writeError = false;
GwBuffer *readBuffer=NULL; bool allowRead;
GwLog *logger; GwBuffer *buffer = NULL;
public: GwBuffer *readBuffer = NULL;
GwClient(wiFiClientPtr client,GwLog *logger,int id, bool allowRead=false){ GwLog *logger;
this->client=client;
this->logger=logger; public:
this->allowRead=allowRead; static IPAddress remoteIP(int fd)
String bufName="Sock("; {
bufName+=String(id); struct sockaddr_storage addr;
bufName+=")"; socklen_t len = sizeof addr;
buffer=new GwBuffer(logger,GwBuffer::TX_BUFFER_SIZE,bufName+"wr"); getpeername(fd, (struct sockaddr*)&addr, &len);
if (allowRead){ struct sockaddr_in *s = (struct sockaddr_in *)&addr;
readBuffer=new GwBuffer(logger,GwBuffer::RX_BUFFER_SIZE,bufName+"rd"); return IPAddress((uint32_t)(s->sin_addr.s_addr));
} }
overflows=0; GwClient(GwLog *logger, int id, bool allowRead = false)
if (client != NULL){ {
remoteIp=client->remoteIP().toString(); this->logger = logger;
} this->allowRead = allowRead;
String bufName = "Sock(";
bufName += String(id);
bufName += ")";
buffer = new GwBuffer(logger, GwBuffer::TX_BUFFER_SIZE, bufName + "wr");
if (allowRead)
{
readBuffer = new GwBuffer(logger, GwBuffer::RX_BUFFER_SIZE, bufName + "rd");
} }
void setClient(wiFiClientPtr client){ overflows = 0;
this->client=client; }
buffer->reset("new client"); void setClient(int fd)
if (readBuffer) readBuffer->reset("new client"); {
overflows=0; this->fd = fd;
pendingWrite=false; buffer->reset("new client");
writeError=false; if (readBuffer)
lastWrite=0; readBuffer->reset("new client");
if (client){ overflows = 0;
remoteIp=client->remoteIP().toString(); pendingWrite = false;
} writeError = false;
else{ lastWrite = 0;
remoteIp=String("---"); if (fd >= 0)
} {
remoteIpAddress = remoteIP(fd).toString();
} }
bool hasClient(){ else
return client != NULL; {
remoteIpAddress = String("---");
} }
~GwClient(){ }
delete buffer; bool hasClient()
if (readBuffer) delete readBuffer; {
return fd >= 0;
}
void stop(){
if (fd >= 0){
close(fd);
fd=-1;
} }
bool enqueue(uint8_t *data, size_t len){ }
if (len == 0) return true; ~GwClient()
size_t rt=buffer->addData(data,len); {
if (rt < len){ delete buffer;
LOG_DEBUG(GwLog::LOG,"overflow on %s",remoteIp.c_str()); if (readBuffer)
overflows++; delete readBuffer;
return false; }
} bool connected()
return true; {
} if (fd >= 0)
bool hasData(){ {
return buffer->usedSpace() > 0; uint8_t dummy;
} int res = recv(fd, &dummy, 0, MSG_DONTWAIT);
bool handleError(int res,bool errorIf0=true){ // avoid unused var warning by gcc
if (res == 0 && errorIf0){ (void)res;
LOG_DEBUG(GwLog::LOG,"client shutdown (recv 0) on %s",remoteIp.c_str()); // recv only sets errno if res is <= 0
client->stop(); if (res <= 0)
return false; {
} switch (errno)
if (res < 0){ {
if (errno != EAGAIN){ case EWOULDBLOCK:
LOG_DEBUG(GwLog::LOG,"client read error %d on %s",errno,remoteIp.c_str()); case ENOENT: //caused by vfs
client->stop(); return true;
break;
case ENOTCONN:
case EPIPE:
case ECONNRESET:
case ECONNREFUSED:
case ECONNABORTED:
return false; return false;
break;
default:
return true;
} }
}
else
{
return true;
}
}
return false;
}
bool enqueue(uint8_t *data, size_t len)
{
if (len == 0)
return true;
size_t rt = buffer->addData(data, len);
if (rt < len)
{
LOG_DEBUG(GwLog::LOG, "overflow on %s", remoteIpAddress.c_str());
overflows++;
return false;
}
return true;
}
bool hasData()
{
return buffer->usedSpace() > 0;
}
bool handleError(int res, bool errorIf0 = true)
{
if (res == 0 && errorIf0)
{
LOG_DEBUG(GwLog::LOG, "client shutdown (recv 0) on %s", remoteIpAddress.c_str());
stop();
return false;
}
if (res < 0)
{
if (errno != EAGAIN)
{
LOG_DEBUG(GwLog::LOG, "client read error %d on %s", errno, remoteIpAddress.c_str());
stop();
return false; return false;
} }
return true; return false;
} }
GwBuffer::WriteStatus write(){ return true;
if (! hasClient()) { }
LOG_DEBUG(GwLog::LOG,"write called on empty client"); GwBuffer::WriteStatus write()
return GwBuffer::ERROR; {
} if (!hasClient())
if (! buffer->usedSpace()){ {
pendingWrite=false; LOG_DEBUG(GwLog::LOG, "write called on empty client");
return GwBuffer::OK; return GwBuffer::ERROR;
} }
buffer->fetchData(-1,[](uint8_t *buffer, size_t len, void *param)->size_t{ if (!buffer->usedSpace())
GwClient *c=(GwClient*)param; {
int res = send(c->client->fd(), (void*) buffer, len, MSG_DONTWAIT); pendingWrite = false;
if (! c->handleError(res,false)) return 0; return GwBuffer::OK;
if (res >= len){ }
c->pendingWrite=false; buffer->fetchData(
-1, [](uint8_t *buffer, size_t len, void *param) -> size_t
{
GwClient *c = (GwClient *)param;
int res = send(c->fd, (void *)buffer, len, MSG_DONTWAIT);
if (!c->handleError(res, false))
return 0;
if (res >= len)
{
c->pendingWrite = false;
} }
else{ else
if (!c->pendingWrite){ {
c->lastWrite=millis(); if (!c->pendingWrite)
c->pendingWrite=true; {
c->lastWrite = millis();
c->pendingWrite = true;
} }
else{ else
//we need to check if we have still not been able {
//we need to check if we have still not been able
//to write until timeout //to write until timeout
if (millis() >= (c->lastWrite+c->writeTimeout)){ if (millis() >= (c->lastWrite + c->writeTimeout))
c->logger->logDebug(GwLog::ERROR,"Write timeout on channel %s",c->remoteIp.c_str()); {
c->writeError=true; c->logger->logDebug(GwLog::ERROR, "Write timeout on channel %s", c->remoteIpAddress.c_str());
c->writeError = true;
} }
} }
} }
return res; return res;
},this); },
if (writeError){ this);
LOG_DEBUG(GwLog::DEBUG+1,"write error on %s",remoteIp.c_str()); if (writeError)
return GwBuffer::ERROR; {
} LOG_DEBUG(GwLog::DEBUG + 1, "write error on %s", remoteIpAddress.c_str());
return GwBuffer::ERROR;
return GwBuffer::OK;
} }
bool read(){ return GwBuffer::OK;
if (! allowRead){ }
size_t maxLen=100;
char buffer[maxLen]; bool read()
int res = recv(client->fd(), (void*) buffer, maxLen, MSG_DONTWAIT); {
return handleError(res); if (!allowRead)
} {
readBuffer->fillData(-1,[](uint8_t *buffer, size_t len, void *param)->size_t{ size_t maxLen = 100;
GwClient *c=(GwClient*)param; char buffer[maxLen];
int res = recv(c->client->fd(), (void*) buffer, len, MSG_DONTWAIT); int res = recv(fd, (void *)buffer, maxLen, MSG_DONTWAIT);
if (! c->handleError(res)) return 0; return handleError(res);
}
readBuffer->fillData(
-1, [](uint8_t *buffer, size_t len, void *param) -> size_t
{
GwClient *c = (GwClient *)param;
int res = recv(c->fd, (void *)buffer, len, MSG_DONTWAIT);
if (!c->handleError(res))
return 0;
return res; return res;
},this); },
return true; this);
} return true;
bool messagesFromBuffer(GwMessageFetcher *writer){ }
if (! allowRead) return false; bool messagesFromBuffer(GwMessageFetcher *writer)
return writer->handleBuffer(readBuffer); {
} if (!allowRead)
return false;
return writer->handleBuffer(readBuffer);
}
}; };
class GwTcpClient
GwSocketServer::GwSocketServer(const GwConfigHandler *config,GwLog *logger,int minId){
this->config=config;
this->logger=logger;
this->minId=minId;
maxClients=1;
allowReceive=false;
}
void GwSocketServer::begin(){
maxClients=config->getInt(config->maxClients);
allowReceive=config->getBool(config->readTCP);
clients=new gwClientPtr[maxClients];
for (int i=0;i<maxClients;i++){
clients[i]=gwClientPtr(new GwClient(wiFiClientPtr(NULL),logger,i,allowReceive));
}
server=new WiFiServer(config->getInt(config->serverPort),maxClients+1);
server->begin();
LOG_DEBUG(GwLog::LOG,"Socket server created, port=%d",
config->getInt(config->serverPort));
MDNS.addService("_nmea-0183","_tcp",config->getInt(config->serverPort));
}
void GwSocketServer::loop(bool handleRead,bool handleWrite)
{ {
if (! clients) return; GwClient *gwClient = NULL;
WiFiClient client = server->available(); // listen for incoming clients IPAddress remoteAddress;
uint16_t port = 0;
GwLog *logger;
if (client) public:
typedef enum
{ {
LOG_DEBUG(GwLog::LOG,"new client connected from %s", C_DISABLED = 0,
client.remoteIP().toString().c_str()); C_INITIALIZED = 1,
fcntl(client.fd(), F_SETFL, O_NONBLOCK); C_CONNECTING = 2,
C_CONNECTED = 3
} State;
private:
State state = C_DISABLED;
void stop()
{
if (gwClient->hasClient())
{
LOG_DEBUG(GwLog::DEBUG, "stopping tcp client");
gwClient->stop();
}
state = C_DISABLED;
}
void startConnection()
{
//TODO
state = C_CONNECTING;
}
void checkConnection()
{
}
public:
GwTcpClient(GwLog *logger, GwClient *gwClient)
{
this->logger = logger;
this->gwClient = gwClient;
}
void begin(IPAddress address, uint16_t port)
{
stop();
this->remoteAddress = address;
this->port = port;
state = C_INITIALIZED;
startConnection();
}
void loop()
{
if (state == C_CONNECTING)
{
checkConnection();
}
}
};
GwSocketServer::GwSocketServer(const GwConfigHandler *config, GwLog *logger, int minId)
{
this->config = config;
this->logger = logger;
this->minId = minId;
maxClients = 1;
allowReceive = false;
}
bool GwSocketServer::createListener()
{
struct sockaddr_in server;
listener = socket(AF_INET, SOCK_STREAM, 0);
if (listener < 0)
{
return false;
}
int enable = 1;
setsockopt(listener, SOL_SOCKET, SO_REUSEADDR, &enable, sizeof(int));
server.sin_family = AF_INET;
server.sin_addr.s_addr = INADDR_ANY;
server.sin_port = htons(listenerPort);
if (bind(listener, (struct sockaddr *)&server, sizeof(server)) < 0)
return false;
if (listen(listener, maxClients) < 0)
return false;
fcntl(listener, F_SETFL, O_NONBLOCK);
return true;
}
void GwSocketServer::begin()
{
maxClients = config->getInt(config->maxClients);
allowReceive = config->getBool(config->readTCP);
listenerPort=config->getInt(config->serverPort);
clients = new GwClient*[maxClients];
for (int i = 0; i < maxClients; i++)
{
clients[i] = new GwClient(logger, i, allowReceive);
}
if (! createListener()){
listener=-1;
LOG_DEBUG(GwLog::ERROR,"Unable to create listener");
return;
}
LOG_DEBUG(GwLog::LOG, "Socket server created, port=%d",
config->getInt(config->serverPort));
MDNS.addService("_nmea-0183", "_tcp", config->getInt(config->serverPort));
}
int GwSocketServer::available()
{
if (listener < 0)
return -1;
int client_sock;
struct sockaddr_in _client;
int cs = sizeof(struct sockaddr_in);
client_sock = lwip_accept_r(listener, (struct sockaddr *)&_client, (socklen_t *)&cs);
if (client_sock >= 0)
{
int val = 1;
if (setsockopt(client_sock, SOL_SOCKET, SO_KEEPALIVE, (char *)&val, sizeof(int)) == ESP_OK)
{
if (setsockopt(client_sock, IPPROTO_TCP, TCP_NODELAY, (char *)&val, sizeof(int)) == ESP_OK)
fcntl(client_sock, F_SETFL, O_NONBLOCK);
return client_sock;
}
close(client_sock);
}
return -1;
}
void GwSocketServer::loop(bool handleRead, bool handleWrite)
{
if (!clients)
return;
int client = available(); // listen for incoming clients
if (client >= 0)
{
LOG_DEBUG(GwLog::LOG, "new client connected from %s",
GwClient::remoteIP(client).toString().c_str());
bool canHandle = false; bool canHandle = false;
for (int i = 0; i < maxClients; i++) for (int i = 0; i < maxClients; i++)
{ {
if (!clients[i]->hasClient()) if (!clients[i]->hasClient())
{ {
clients[i]->setClient(wiFiClientPtr(new WiFiClient(client))); clients[i]->setClient(client);
LOG_DEBUG(GwLog::LOG,"set client as number %d", i); LOG_DEBUG(GwLog::LOG, "set client as number %d", i);
canHandle = true; canHandle = true;
break; break;
} }
@ -192,7 +383,7 @@ void GwSocketServer::loop(bool handleRead,bool handleWrite)
if (!canHandle) if (!canHandle)
{ {
logger->logDebug(GwLog::ERROR, "no space to store client, disconnect"); logger->logDebug(GwLog::ERROR, "no space to store client, disconnect");
client.stop(); close(client);
} }
} }
if (handleWrite) if (handleWrite)
@ -200,69 +391,83 @@ void GwSocketServer::loop(bool handleRead,bool handleWrite)
//sending //sending
for (int i = 0; i < maxClients; i++) for (int i = 0; i < maxClients; i++)
{ {
gwClientPtr client = clients[i]; GwClient *client = clients[i];
if (!client->hasClient()) if (!client->hasClient())
continue; continue;
GwBuffer::WriteStatus rt = client->write(); GwBuffer::WriteStatus rt = client->write();
if (rt == GwBuffer::ERROR) if (rt == GwBuffer::ERROR)
{ {
LOG_DEBUG(GwLog::ERROR, "write error on %s, closing", client->remoteIp.c_str()); LOG_DEBUG(GwLog::ERROR, "write error on %s, closing", client->remoteIpAddress.c_str());
client->client->stop(); client->stop();
} }
} }
} }
for (int i = 0; i < maxClients; i++) for (int i = 0; i < maxClients; i++)
{ {
gwClientPtr client = clients[i]; GwClient *client = clients[i];
if (!client->hasClient()) if (!client->hasClient())
continue; continue;
if (!client->client->connected()) if (!client->connected())
{ {
LOG_DEBUG(GwLog::LOG,"client %d disconnect %s", i, client->remoteIp.c_str()); LOG_DEBUG(GwLog::LOG, "client %d disconnect %s", i, client->remoteIpAddress.c_str());
client->client->stop(); client->stop();
client->setClient(NULL);
} }
else else
{ {
if (handleRead) client->read(); if (handleRead)
client->read();
} }
} }
} }
bool GwSocketServer::readMessages(GwMessageFetcher *writer){ bool GwSocketServer::readMessages(GwMessageFetcher *writer)
if (! allowReceive || ! clients) return false; {
bool hasMessages=false; if (!allowReceive || !clients)
for (int i = 0; i < maxClients; i++){ return false;
writer->id=minId+i; bool hasMessages = false;
if (!clients[i]->hasClient()) continue; for (int i = 0; i < maxClients; i++)
if (clients[i]->messagesFromBuffer(writer)) hasMessages=true; {
writer->id = minId + i;
if (!clients[i]->hasClient())
continue;
if (clients[i]->messagesFromBuffer(writer))
hasMessages = true;
} }
return hasMessages; return hasMessages;
} }
void GwSocketServer::sendToClients(const char *buf,int source){ void GwSocketServer::sendToClients(const char *buf, int source)
if (! clients) return; {
int len=strlen(buf); if (!clients)
int sourceIndex=source-minId; return;
int len = strlen(buf);
int sourceIndex = source - minId;
for (int i = 0; i < maxClients; i++) for (int i = 0; i < maxClients; i++)
{ {
if (i == sourceIndex)continue; //never send out to the source we received from if (i == sourceIndex)
gwClientPtr client = clients[i]; continue; //never send out to the source we received from
if (! client->hasClient()) continue; GwClient *client = clients[i];
if ( client->client->connected() ) { if (!client->hasClient())
client->enqueue((uint8_t*)buf,len); continue;
if (client->connected())
{
client->enqueue((uint8_t *)buf, len);
} }
} }
} }
int GwSocketServer::numClients(){ int GwSocketServer::numClients()
if (! clients) return 0; {
int num=0; if (!clients)
for (int i = 0; i < maxClients; i++){ return 0;
if (clients[i]->hasClient()) num++; int num = 0;
for (int i = 0; i < maxClients; i++)
{
if (clients[i]->hasClient())
num++;
} }
return num; return num;
} }
GwSocketServer::~GwSocketServer(){ GwSocketServer::~GwSocketServer()
{
} }

View File

@ -6,18 +6,19 @@
#include <memory> #include <memory>
#include <WiFi.h> #include <WiFi.h>
using wiFiClientPtr = std::shared_ptr<WiFiClient>;
class GwClient; class GwClient;
using gwClientPtr = std::shared_ptr<GwClient>;
class GwSocketServer{ class GwSocketServer{
private: private:
const GwConfigHandler *config; const GwConfigHandler *config;
GwLog *logger; GwLog *logger;
gwClientPtr *clients=NULL; GwClient **clients=NULL;
WiFiServer *server=NULL; int listener=-1;
int listenerPort=-1;
bool allowReceive; bool allowReceive;
int maxClients; int maxClients;
int minId; int minId;
bool createListener();
int available();
public: public:
GwSocketServer(const GwConfigHandler *config,GwLog *logger,int minId); GwSocketServer(const GwConfigHandler *config,GwLog *logger,int minId);
~GwSocketServer(); ~GwSocketServer();