introduce tcp client
This commit is contained in:
parent
31798e3bec
commit
0ba05accdc
|
@ -4,65 +4,6 @@
|
|||
#include "GwBuffer.h"
|
||||
#include "GwSocketConnection.h"
|
||||
|
||||
class GwTcpClient
|
||||
{
|
||||
GwSocketConnection *gwClient = NULL;
|
||||
IPAddress remoteAddress;
|
||||
uint16_t port = 0;
|
||||
GwLog *logger;
|
||||
|
||||
public:
|
||||
typedef enum
|
||||
{
|
||||
C_DISABLED = 0,
|
||||
C_INITIALIZED = 1,
|
||||
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, GwSocketConnection *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;
|
||||
|
|
|
@ -0,0 +1,164 @@
|
|||
#include "GwTcpClient.h"
|
||||
|
||||
bool GwTcpClient::hasConfig(){
|
||||
return configured;
|
||||
}
|
||||
bool GwTcpClient::isConnected(){
|
||||
return state == C_CONNECTED;
|
||||
}
|
||||
void GwTcpClient::stop()
|
||||
{
|
||||
if (connection->hasClient())
|
||||
{
|
||||
LOG_DEBUG(GwLog::DEBUG, "stopping tcp client");
|
||||
connection->stop();
|
||||
}
|
||||
state = C_DISABLED;
|
||||
}
|
||||
void GwTcpClient::startConnection()
|
||||
{
|
||||
//TODO
|
||||
state = C_INITIALIZED;
|
||||
connectStart=millis();
|
||||
int sockfd = socket(AF_INET, SOCK_STREAM, 0);
|
||||
if (sockfd < 0) {
|
||||
LOG_DEBUG(GwLog::ERROR,"unable to create socket: %d", errno);
|
||||
return;
|
||||
}
|
||||
fcntl( sockfd, F_SETFL, fcntl( sockfd, F_GETFL, 0 ) | O_NONBLOCK );
|
||||
|
||||
uint32_t ip_addr = this->remoteAddress;
|
||||
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);
|
||||
int res = lwip_connect_r(sockfd, (struct sockaddr*)&serveraddr, sizeof(serveraddr));
|
||||
if (res < 0 ) {
|
||||
if (errno != EINPROGRESS){
|
||||
LOG_DEBUG(GwLog::ERROR,"connect on fd %d, errno: %d, \"%s\"", sockfd, errno, strerror(errno));
|
||||
close(sockfd);
|
||||
return;
|
||||
}
|
||||
state=C_CONNECTING;
|
||||
connection->setClient(sockfd);
|
||||
}
|
||||
else{
|
||||
state=C_CONNECTED;
|
||||
connection->setClient(sockfd);
|
||||
}
|
||||
}
|
||||
void GwTcpClient::checkConnection()
|
||||
{
|
||||
unsigned long now=millis();
|
||||
if (! connection->hasClient()){
|
||||
state = hasConfig()?C_INITIALIZED:C_DISABLED;
|
||||
}
|
||||
if (state == C_INITIALIZED){
|
||||
if ((now - connectStart) > CON_TIMEOUT){
|
||||
LOG_DEBUG(GwLog::LOG,"retry connect to %s",remoteAddress.toString().c_str());
|
||||
startConnection();
|
||||
}
|
||||
return;
|
||||
}
|
||||
if (state != C_CONNECTING){
|
||||
return;
|
||||
}
|
||||
fd_set fdset;
|
||||
struct timeval tv;
|
||||
FD_ZERO(&fdset);
|
||||
int sockfd=connection->fd;
|
||||
FD_SET(connection->fd, &fdset);
|
||||
tv.tv_sec = 0;
|
||||
tv.tv_usec = 0;
|
||||
int res = select(sockfd + 1, nullptr, &fdset, nullptr, &tv);
|
||||
if (res < 0) {
|
||||
LOG_DEBUG(GwLog::ERROR,"select on fd %d, errno: %d, \"%s\"", sockfd, errno, strerror(errno));
|
||||
connection->stop();
|
||||
return;
|
||||
} else if (res == 0) {
|
||||
//still connecting
|
||||
if ((now - connectStart) >= CON_TIMEOUT){
|
||||
LOG_DEBUG(GwLog::ERROR,"connect timeout to %s, retry",remoteAddress.toString().c_str());
|
||||
connection->stop();
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
int sockerr;
|
||||
socklen_t len = (socklen_t)sizeof(int);
|
||||
res = getsockopt(sockfd, SOL_SOCKET, SO_ERROR, &sockerr, &len);
|
||||
|
||||
if (res < 0) {
|
||||
LOG_DEBUG(GwLog::ERROR,"getsockopt on fd %d, errno: %d, \"%s\"", sockfd, errno, strerror(errno));
|
||||
connection->stop();
|
||||
return;
|
||||
}
|
||||
if (sockerr != 0) {
|
||||
LOG_DEBUG(GwLog::ERROR,"socket error on fd %d, errno: %d, \"%s\"", sockfd, sockerr, strerror(sockerr));
|
||||
connection->stop();
|
||||
return;
|
||||
}
|
||||
}
|
||||
LOG_DEBUG(GwLog::LOG,"connected to %s",remoteAddress.toString().c_str());
|
||||
state=C_CONNECTED;
|
||||
}
|
||||
|
||||
GwTcpClient::GwTcpClient(GwLog *logger)
|
||||
{
|
||||
this->logger = logger;
|
||||
}
|
||||
GwTcpClient::~GwTcpClient(){
|
||||
if (connection)
|
||||
delete connection;
|
||||
}
|
||||
void GwTcpClient::begin(int sourceId,IPAddress address, uint16_t port,bool allowRead)
|
||||
{
|
||||
stop();
|
||||
this->sourceId=sourceId;
|
||||
this->remoteAddress = address;
|
||||
this->port = port;
|
||||
configured=true;
|
||||
state = C_INITIALIZED;
|
||||
this->connection = new GwSocketConnection(logger,0,allowRead);
|
||||
startConnection();
|
||||
}
|
||||
void GwTcpClient::loop(bool handleRead,bool handleWrite)
|
||||
{
|
||||
checkConnection();
|
||||
if (state != C_CONNECTED){
|
||||
return;
|
||||
}
|
||||
if (handleRead){
|
||||
if (connection->hasClient()){
|
||||
if (! connection->connected()){
|
||||
LOG_DEBUG(GwLog::ERROR,"connection closed on %s",connection->remoteIpAddress.c_str());
|
||||
connection->stop();
|
||||
}
|
||||
else{
|
||||
connection->read();
|
||||
}
|
||||
}
|
||||
}
|
||||
if (handleWrite){
|
||||
if (connection->hasClient()){
|
||||
GwBuffer::WriteStatus rt = connection->write();
|
||||
if (rt == GwBuffer::ERROR)
|
||||
{
|
||||
LOG_DEBUG(GwLog::ERROR, "write error on %s, closing", connection->remoteIpAddress.c_str());
|
||||
connection->stop();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void GwTcpClient::sendToClients(const char *buf,int sourceId){
|
||||
if (sourceId == this->sourceId) return;
|
||||
if (state != C_CONNECTED) return;
|
||||
if (! connection->hasClient()) return;
|
||||
connection->enqueue((uint8_t*)buf,strlen(buf));
|
||||
}
|
||||
bool GwTcpClient::readMessages(GwMessageFetcher *writer){
|
||||
if (state != C_CONNECTED) return false;
|
||||
if (! connection->hasClient()) return false;
|
||||
return connection->messagesFromBuffer(writer);
|
||||
}
|
|
@ -0,0 +1,38 @@
|
|||
#pragma once
|
||||
#include "GwSocketConnection.h"
|
||||
class GwTcpClient
|
||||
{
|
||||
static const unsigned long CON_TIMEOUT=10;
|
||||
GwSocketConnection *connection = NULL;
|
||||
IPAddress remoteAddress;
|
||||
uint16_t port = 0;
|
||||
unsigned long connectStart=0;
|
||||
GwLog *logger;
|
||||
int sourceId;
|
||||
bool configured=false;
|
||||
|
||||
public:
|
||||
typedef enum
|
||||
{
|
||||
C_DISABLED = 0,
|
||||
C_INITIALIZED = 1,
|
||||
C_CONNECTING = 2,
|
||||
C_CONNECTED = 3
|
||||
} State;
|
||||
|
||||
private:
|
||||
State state = C_DISABLED;
|
||||
void stop();
|
||||
void startConnection();
|
||||
void checkConnection();
|
||||
bool hasConfig();
|
||||
|
||||
public:
|
||||
GwTcpClient(GwLog *logger);
|
||||
~GwTcpClient();
|
||||
void begin(int sourceId,IPAddress address, uint16_t port,bool allowRead);
|
||||
void loop(bool handleRead=true,bool handleWrite=true);
|
||||
void sendToClients(const char *buf,int sourceId);
|
||||
bool readMessages(GwMessageFetcher *writer);
|
||||
bool isConnected();
|
||||
};
|
|
@ -376,7 +376,7 @@
|
|||
"type": "number",
|
||||
"default": "10110",
|
||||
"description": "the TCP port we listen on",
|
||||
"category": "TCP port"
|
||||
"category": "TCP server"
|
||||
},
|
||||
{
|
||||
"name": "maxClients",
|
||||
|
@ -387,7 +387,7 @@
|
|||
"min": 0,
|
||||
"max": 6,
|
||||
"description": "the number of clients we allow to connect to us",
|
||||
"category": "TCP port"
|
||||
"category": "TCP server"
|
||||
},
|
||||
{
|
||||
"name": "sendTCP",
|
||||
|
@ -395,7 +395,7 @@
|
|||
"type": "boolean",
|
||||
"default": "true",
|
||||
"description": "send out NMEA data to connected TCP clients",
|
||||
"category": "TCP port"
|
||||
"category": "TCP server"
|
||||
},
|
||||
{
|
||||
"name": "readTCP",
|
||||
|
@ -403,7 +403,7 @@
|
|||
"type": "boolean",
|
||||
"default": "true",
|
||||
"description": "receive NMEA data from connected TCP clients",
|
||||
"category": "TCP port"
|
||||
"category": "TCP server"
|
||||
},
|
||||
{
|
||||
"name": "tcpToN2k",
|
||||
|
@ -411,7 +411,7 @@
|
|||
"type": "boolean",
|
||||
"default": "true",
|
||||
"description": "convert NMEA0183 from TCP clients to NMEA2000",
|
||||
"category": "TCP port"
|
||||
"category": "TCP server"
|
||||
},
|
||||
{
|
||||
"name": "tcpReadFilter",
|
||||
|
@ -419,7 +419,7 @@
|
|||
"type": "filter",
|
||||
"default": "",
|
||||
"description": "filter for NMEA0183 data when reading from TCP\nselect aison|aisoff, set a whitelist or a blacklist with NMEA sentences like RMC,RMB",
|
||||
"category": "TCP port"
|
||||
"category": "TCP server"
|
||||
},
|
||||
{
|
||||
"name": "tcpWriteFilter",
|
||||
|
@ -427,7 +427,7 @@
|
|||
"type": "filter",
|
||||
"default": "",
|
||||
"description": "filter for NMEA0183 data when writing to TCP\nselect aison|aisoff, set a whitelist or a blacklist with NMEA sentences like RMC,RMB",
|
||||
"category": "TCP port"
|
||||
"category": "TCP server"
|
||||
},
|
||||
{
|
||||
"name": "sendSeasmart",
|
||||
|
@ -435,7 +435,80 @@
|
|||
"type": "boolean",
|
||||
"default": "false",
|
||||
"description": "send NMEA2000 as seasmart to connected TCP clients",
|
||||
"category": "TCP port"
|
||||
"category": "TCP server"
|
||||
},
|
||||
{
|
||||
"name": "tclEnabled",
|
||||
"label": "enable TCP client",
|
||||
"type": "boolean",
|
||||
"default": "false",
|
||||
"description":"enable the TCP client",
|
||||
"category":"TCP client"
|
||||
},
|
||||
{
|
||||
"name": "remotePort",
|
||||
"label": "remote port",
|
||||
"type": "number",
|
||||
"default": "10110",
|
||||
"description": "the TCP port we connect to",
|
||||
"category": "TCP client"
|
||||
},
|
||||
{
|
||||
"name": "remoteAddress",
|
||||
"label": "remote address",
|
||||
"type": "string",
|
||||
"default": "",
|
||||
"check": "checkIpAddress",
|
||||
"description": "the IP address we connect to",
|
||||
"category": "TCP client"
|
||||
},
|
||||
{
|
||||
"name": "sendTCL",
|
||||
"label": "NMEA to TCP client",
|
||||
"type": "boolean",
|
||||
"default": "true",
|
||||
"description": "send out NMEA data to remote TCP server",
|
||||
"category": "TCP client"
|
||||
},
|
||||
{
|
||||
"name": "readTCL",
|
||||
"label": "NMEA from TCP client",
|
||||
"type": "boolean",
|
||||
"default": "true",
|
||||
"description": "receive NMEA data from remote TCP server",
|
||||
"category": "TCP client"
|
||||
},
|
||||
{
|
||||
"name": "tclToN2k",
|
||||
"label": "TCP client to NMEA2000",
|
||||
"type": "boolean",
|
||||
"default": "true",
|
||||
"description": "convert NMEA0183 from remote TCP server to NMEA2000",
|
||||
"category": "TCP client"
|
||||
},
|
||||
{
|
||||
"name": "tclReadFilter",
|
||||
"label": "TCP client read Filter",
|
||||
"type": "filter",
|
||||
"default": "",
|
||||
"description": "filter for NMEA0183 data when reading from remote TCP server\nselect aison|aisoff, set a whitelist or a blacklist with NMEA sentences like RMC,RMB",
|
||||
"category": "TCP client"
|
||||
},
|
||||
{
|
||||
"name": "tclWriteFilter",
|
||||
"label": "TCP client write Filter",
|
||||
"type": "filter",
|
||||
"default": "",
|
||||
"description": "filter for NMEA0183 data when writing to remote TCP server\nselect aison|aisoff, set a whitelist or a blacklist with NMEA sentences like RMC,RMB",
|
||||
"category": "TCP client"
|
||||
},
|
||||
{
|
||||
"name": "tclSeasmart",
|
||||
"label": "Seasmart to TCP client",
|
||||
"type": "boolean",
|
||||
"default": "false",
|
||||
"description": "send NMEA2000 as seasmart to remote TCP server",
|
||||
"category": "TCP client"
|
||||
},
|
||||
{
|
||||
"name": "wifiClient",
|
||||
|
|
Loading…
Reference in New Issue