350 lines
7.0 KiB
C++
350 lines
7.0 KiB
C++
#ifndef TCP_H
|
|
#define TCP_H
|
|
|
|
|
|
#include "../Platforms.h"
|
|
|
|
class TCP;
|
|
|
|
typedef void (*TCPDataCallback)(TCP* tcp, const uint8_t* data, uint16_t len);
|
|
typedef void (*TCPConnectCallback)(TCP* tcp);
|
|
typedef void (*TCPDisconnectCallback)(TCP* tcp);
|
|
|
|
#include "../Debug.h"
|
|
#include "IP.h"
|
|
|
|
#if ESP8266
|
|
|
|
extern "C" {
|
|
#include "mem.h"
|
|
#include "espconn.h"
|
|
}
|
|
|
|
class TCP {
|
|
|
|
private:
|
|
|
|
static constexpr const char* NAME = "TCP";
|
|
|
|
espconn* con;
|
|
esp_tcp tcp;
|
|
bool connected = false;
|
|
|
|
TCPDataCallback onData = nullptr;
|
|
TCPConnectCallback onConnect = nullptr;
|
|
TCPDisconnectCallback onDisconnect = nullptr;
|
|
void* userData = nullptr;
|
|
|
|
public:
|
|
|
|
TCP() {
|
|
init();
|
|
}
|
|
|
|
/** dtor */
|
|
~TCP() {
|
|
cleanup();
|
|
}
|
|
|
|
|
|
/** send data to the other side */
|
|
bool send(const uint8_t* data, const size_t dataLen) {
|
|
const int res = espconn_sent(con, (unsigned char*)data, dataLen);
|
|
return (res == 0);
|
|
}
|
|
|
|
/** connect to the given IP and port */
|
|
void connect(const IP ip, const Port port) {
|
|
disconnect();
|
|
debugMod2(NAME, "connect(%s, %d)", ip.toString(), port);
|
|
con->proto.tcp->remote_port = port;
|
|
con->proto.tcp->local_port = espconn_port();
|
|
os_memcpy(con->proto.tcp->remote_ip, ip.getPtr(), 4);
|
|
espconn_connect(con);
|
|
connected = true;
|
|
}
|
|
|
|
|
|
/** terminate connection */
|
|
void disconnect() {
|
|
if (!connected) {return;}
|
|
espconn_disconnect(con);
|
|
connected = false;
|
|
}
|
|
|
|
void hold() {
|
|
if (connected) {
|
|
espconn_recv_hold(con);
|
|
}
|
|
}
|
|
|
|
void unhold() {
|
|
if (connected) {
|
|
espconn_recv_unhold(con);
|
|
}
|
|
}
|
|
|
|
/** set the callback to call whenever data is received */
|
|
void setDataCallback(TCPDataCallback callback) {
|
|
this->onData = callback;
|
|
}
|
|
|
|
/** set the callback to call when connection is established */
|
|
void setConnectCallback(TCPConnectCallback callback) {
|
|
this->onConnect = callback;
|
|
}
|
|
|
|
/** set the callback to call when connection is lost */
|
|
void setDisconnectCallback(TCPDisconnectCallback callback) {
|
|
this->onDisconnect = callback;
|
|
}
|
|
|
|
/** attach user-data to this object. can bed used within callbacks */
|
|
void setUserData(void* ptr) {
|
|
this->userData = ptr;
|
|
}
|
|
|
|
/** get previously attached user data */
|
|
void* getUserData() const {
|
|
return this->userData;
|
|
}
|
|
|
|
private:
|
|
|
|
/** called for incoming data */
|
|
static void _onData(void* ptr, char* data, unsigned short len) {
|
|
TCP* tcp = (TCP*) ((espconn*)ptr)->reverse;
|
|
if (tcp->onData) {tcp->onData(tcp, (const uint8_t*)data, len);}
|
|
}
|
|
|
|
/** called when connection is established */
|
|
static void _onConnect(void* ptr) {
|
|
TCP* tcp = (TCP*) ((espconn*)ptr)->reverse;
|
|
if (tcp->onConnect) {tcp->onConnect(tcp);}
|
|
tcp->connected = true;
|
|
}
|
|
|
|
/** called when connection is lost */
|
|
static void _onDisconnect(void* ptr) {
|
|
TCP* tcp = (TCP*) ((espconn*)ptr)->reverse;
|
|
if (tcp->onDisconnect) {tcp->onDisconnect(tcp);}
|
|
tcp->connected = false;
|
|
}
|
|
|
|
|
|
|
|
/** initialize the UDP "connection" */
|
|
void init() {
|
|
|
|
debugMod(NAME, "init()");
|
|
|
|
// allocate connection-objects
|
|
con = (espconn*) os_zalloc(sizeof(espconn));
|
|
ets_memset( con, 0, sizeof( espconn ) );
|
|
|
|
// configure
|
|
con->type = ESPCONN_TCP;
|
|
con->state = ESPCONN_NONE;
|
|
|
|
//con->proto.tcp = (esp_tcp*) os_zalloc(sizeof(esp_tcp));
|
|
con->proto.tcp = &tcp;
|
|
|
|
// attach ourselves for the callback
|
|
con->reverse = (void*) this; // user-data to refer to this class
|
|
espconn_regist_recvcb(con, _onData);
|
|
espconn_regist_connectcb(con, _onConnect);
|
|
espconn_regist_disconcb(con, _onDisconnect);
|
|
// espconn_regist_reconcb(con, _onDisconnect);
|
|
|
|
}
|
|
|
|
/** cleanup everything */
|
|
void cleanup() {
|
|
|
|
debugMod(NAME, "cleanup()");
|
|
|
|
espconn_delete(con);
|
|
//os_free(con->proto.tcp);
|
|
os_free(con);
|
|
con = nullptr;
|
|
|
|
}
|
|
|
|
};
|
|
|
|
#elif ESP32
|
|
|
|
#include "lwip/err.h"
|
|
#include "lwip/tcp.h"
|
|
#include "lwip/ip_addr.h"
|
|
|
|
class TCP {
|
|
|
|
private:
|
|
|
|
static constexpr const char* NAME = "TCP";
|
|
|
|
struct tcp_pcb* pcb;
|
|
|
|
bool connected = false;
|
|
|
|
TCPDataCallback onData = nullptr;
|
|
TCPConnectCallback onConnect = nullptr;
|
|
TCPDisconnectCallback onDisconnect = nullptr;
|
|
void* userData = nullptr;
|
|
|
|
public:
|
|
|
|
TCP() {
|
|
init();
|
|
}
|
|
|
|
/** dtor */
|
|
~TCP() {
|
|
cleanup();
|
|
}
|
|
|
|
|
|
/** send data to the other side */
|
|
bool send(const uint8_t* data, const size_t dataLen) {
|
|
err_t res = tcp_write(pcb, data, dataLen, TCP_WRITE_FLAG_COPY);
|
|
return (res == ERR_OK);
|
|
}
|
|
|
|
/** connect to the given IP and port */
|
|
void connect(const IP4 ip, const Port port) {
|
|
debugMod2(NAME, "connect(%s, %d)", ip.toString(), port);
|
|
disconnect();
|
|
tcp_connect(pcb, ip.getPtr(), port, _onConnectCallback);
|
|
}
|
|
|
|
|
|
/** terminate connection */
|
|
void disconnect() {
|
|
if (!connected) {return;}
|
|
tcp_close(pcb);
|
|
connected = false;
|
|
}
|
|
|
|
void hold() {
|
|
// if (connected) {
|
|
// espconn_recv_hold(con);
|
|
// }
|
|
}
|
|
|
|
void unhold() {
|
|
// if (connected) {
|
|
// espconn_recv_unhold(con);
|
|
// }
|
|
}
|
|
|
|
/** set the callback to call whenever data is received */
|
|
void setDataCallback(TCPDataCallback callback) {
|
|
this->onData = callback;
|
|
}
|
|
|
|
/** set the callback to call when connection is established */
|
|
void setConnectCallback(TCPConnectCallback callback) {
|
|
this->onConnect = callback;
|
|
}
|
|
|
|
/** set the callback to call when connection is lost */
|
|
void setDisconnectCallback(TCPDisconnectCallback callback) {
|
|
this->onDisconnect = callback;
|
|
}
|
|
|
|
/** attach user-data to this object. can bed used within callbacks */
|
|
void setUserData(void* ptr) {
|
|
this->userData = ptr;
|
|
}
|
|
|
|
/** get previously attached user data */
|
|
void* getUserData() const {
|
|
return this->userData;
|
|
}
|
|
|
|
/** inform backend we consumed some data */
|
|
void consumed(uint16_t len) {
|
|
if (len) {
|
|
tcp_recved(pcb, len);
|
|
}
|
|
}
|
|
|
|
private:
|
|
|
|
/** called for incoming data */
|
|
static err_t _onData(void* arg, struct tcp_pcb* tpcb, struct pbuf* p, err_t err) {
|
|
|
|
TCP* tcp = (TCP*) arg;
|
|
|
|
// if p == nullptr -> disconnect -> inform listener
|
|
if (!p) {
|
|
if (tcp->onDisconnect) {tcp->onDisconnect(tcp);}
|
|
return ERR_OK;
|
|
}
|
|
|
|
// process pBuf (might be a chain of buffers)
|
|
do {
|
|
|
|
// pass data to listener
|
|
if (tcp->onData) {tcp->onData(tcp, (const uint8_t*)p->payload, p->len);}
|
|
|
|
// next pBuf in chain (if any)
|
|
pbuf* next = p->next;
|
|
|
|
// free current buffer (we consumed it completely)
|
|
pbuf_free(p);
|
|
|
|
// proceed?
|
|
p = next;
|
|
|
|
} while (p);
|
|
|
|
// everything fine
|
|
return ERR_OK;
|
|
|
|
}
|
|
|
|
/** called when connection failed */
|
|
static err_t _onConnectCallback(void* arg, struct tcp_pcb* tpcb, err_t err) {
|
|
TCP* tcp = (TCP*) arg;
|
|
if (err == ERR_OK) {
|
|
if (tcp->onConnect) {tcp->onConnect(tcp);}
|
|
tcp->connected = true;
|
|
} else {
|
|
if (tcp->onDisconnect) {tcp->onDisconnect(tcp);}
|
|
tcp->connected = false;
|
|
}
|
|
return ERR_OK;
|
|
}
|
|
|
|
/** initialize the UDP "connection" */
|
|
void init() {
|
|
|
|
debugMod(NAME, "init()");
|
|
|
|
pcb = tcp_new();
|
|
if (!pcb) {debugMod(NAME, "error creating TCP socket");}
|
|
|
|
// user-pointer
|
|
pcb->callback_arg = (void*) this;
|
|
|
|
tcp_recv(pcb, _onData);
|
|
|
|
}
|
|
|
|
/** cleanup everything */
|
|
void cleanup() {
|
|
|
|
debugMod(NAME, "cleanup()");
|
|
|
|
// TODO
|
|
|
|
}
|
|
|
|
};
|
|
|
|
#endif
|
|
|
|
#endif // TCP_H
|