mirror of https://github.com/arendst/Tasmota.git
Berry add support for `tcpclientasync` in `tcpserver` (#20401)
This commit is contained in:
parent
0ed01c3b1d
commit
af2b90caac
|
@ -6,6 +6,7 @@ All notable changes to this project will be documented in this file.
|
||||||
## [13.3.0.2]
|
## [13.3.0.2]
|
||||||
### Added
|
### Added
|
||||||
- HASPmota type `chart` (#20372)
|
- HASPmota type `chart` (#20372)
|
||||||
|
- Berry add support for `tcpclientasync` in `tcpserver`
|
||||||
|
|
||||||
### Breaking Changed
|
### Breaking Changed
|
||||||
|
|
||||||
|
|
|
@ -13,7 +13,7 @@ extern void tcpserver_deinit(void *server_tcp); BE_FUNC_
|
||||||
extern void tcpserver_close(void *server); BE_FUNC_CTYPE_DECLARE(tcpserver_close, "", ".")
|
extern void tcpserver_close(void *server); BE_FUNC_CTYPE_DECLARE(tcpserver_close, "", ".")
|
||||||
extern bbool tcpserver_hasclient(void *server); BE_FUNC_CTYPE_DECLARE(tcpserver_hasclient, "b", ".")
|
extern bbool tcpserver_hasclient(void *server); BE_FUNC_CTYPE_DECLARE(tcpserver_hasclient, "b", ".")
|
||||||
extern void * tcpserver_accept(struct bvm *vm, void *server); BE_FUNC_CTYPE_DECLARE(tcpserver_accept, "tcpclient", "@.")
|
extern void * tcpserver_accept(struct bvm *vm, void *server); BE_FUNC_CTYPE_DECLARE(tcpserver_accept, "tcpclient", "@.")
|
||||||
|
extern void * tcpserver_acceptasync(struct bvm *vm, void *server); BE_FUNC_CTYPE_DECLARE(tcpserver_acceptasync, "tcpclientasync", "@.")
|
||||||
|
|
||||||
#include "be_fixed_be_class_tcpserver.h"
|
#include "be_fixed_be_class_tcpserver.h"
|
||||||
|
|
||||||
|
@ -26,6 +26,7 @@ class be_class_tcpserver (scope: global, name: tcpserver) {
|
||||||
close, ctype_func(tcpserver_close)
|
close, ctype_func(tcpserver_close)
|
||||||
hasclient, ctype_func(tcpserver_hasclient)
|
hasclient, ctype_func(tcpserver_hasclient)
|
||||||
accept, ctype_func(tcpserver_accept)
|
accept, ctype_func(tcpserver_accept)
|
||||||
|
acceptasync, ctype_func(tcpserver_acceptasync)
|
||||||
}
|
}
|
||||||
@const_object_info_end */
|
@const_object_info_end */
|
||||||
|
|
||||||
|
|
|
@ -47,6 +47,13 @@ public:
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// following is used when accepting a new connection as server
|
||||||
|
AsyncTCPClient(int fd) : sockfd(fd), state(AsyncTCPState::CONNECTED), _timeout_ms(1), local_port(-1) {
|
||||||
|
if (sockfd < 0) {
|
||||||
|
state = AsyncTCPState::REFUSED;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
~AsyncTCPClient() {
|
~AsyncTCPClient() {
|
||||||
this->stop();
|
this->stop();
|
||||||
}
|
}
|
||||||
|
@ -324,25 +331,58 @@ public:
|
||||||
struct sockaddr_in6 *saddr6 = (struct sockaddr_in6 *)&local_address;
|
struct sockaddr_in6 *saddr6 = (struct sockaddr_in6 *)&local_address;
|
||||||
local_port = ntohs(saddr6->sin6_port);
|
local_port = ntohs(saddr6->sin6_port);
|
||||||
if (T_IN6_IS_ADDR_V4MAPPED(saddr6->sin6_addr.un.u32_addr)) {
|
if (T_IN6_IS_ADDR_V4MAPPED(saddr6->sin6_addr.un.u32_addr)) {
|
||||||
local_addr = IPAddress(IPv4, (uint8_t*)saddr6->sin6_addr.s6_addr+12);
|
local_addr = IPAddress(IPv4, (uint8_t*)saddr6->sin6_addr.s6_addr+12, 0);
|
||||||
} else {
|
} else {
|
||||||
#if ESP_IDF_VERSION_MAJOR >= 5
|
|
||||||
local_addr = IPAddress(IPv6, (uint8_t*)(saddr6->sin6_addr.s6_addr), saddr6->sin6_scope_id);
|
local_addr = IPAddress(IPv6, (uint8_t*)(saddr6->sin6_addr.s6_addr), saddr6->sin6_scope_id);
|
||||||
#else
|
|
||||||
local_addr = IPAddress(IPv6, (uint8_t*)(saddr6->sin6_addr.s6_addr));
|
|
||||||
#endif
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
#endif // USE_IPV6
|
#endif // USE_IPV6
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
IPAddress remoteIP() const {
|
||||||
|
struct sockaddr_storage addr;
|
||||||
|
socklen_t len = sizeof addr;
|
||||||
|
getpeername(sockfd, (struct sockaddr*)&addr, &len);
|
||||||
|
|
||||||
|
// IPv4 socket, old way
|
||||||
|
if (((struct sockaddr*)&addr)->sa_family == AF_INET) {
|
||||||
|
struct sockaddr_in *s = (struct sockaddr_in *)&addr;
|
||||||
|
return IPAddress((uint32_t)(s->sin_addr.s_addr));
|
||||||
|
}
|
||||||
|
|
||||||
|
#if LWIP_IPV6
|
||||||
|
// IPv6, but it might be IPv4 mapped address
|
||||||
|
if (((struct sockaddr*)&addr)->sa_family == AF_INET6) {
|
||||||
|
struct sockaddr_in6 *saddr6 = (struct sockaddr_in6 *)&addr;
|
||||||
|
if (T_IN6_IS_ADDR_V4MAPPED(saddr6->sin6_addr.un.u32_addr)) {
|
||||||
|
return IPAddress(IPv4, (uint8_t*)saddr6->sin6_addr.s6_addr+12, 0);
|
||||||
|
} else {
|
||||||
|
return IPAddress(IPv6, (uint8_t*)(saddr6->sin6_addr.s6_addr), saddr6->sin6_scope_id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
return (IPAddress(0,0,0,0));
|
||||||
|
|
||||||
|
}
|
||||||
|
uint16_t remotePort() const {
|
||||||
|
|
||||||
|
struct sockaddr_storage addr;
|
||||||
|
socklen_t len = sizeof addr;
|
||||||
|
getpeername(sockfd, (struct sockaddr*)&addr, &len);
|
||||||
|
struct sockaddr_in *s = (struct sockaddr_in *)&addr;
|
||||||
|
return ntohs(s->sin_port);
|
||||||
|
}
|
||||||
|
|
||||||
|
const IPAddress localIP() const { return local_addr; }
|
||||||
|
uint16_t localPort() const { return local_port; }
|
||||||
|
|
||||||
|
|
||||||
public:
|
public:
|
||||||
int sockfd;
|
int sockfd;
|
||||||
AsyncTCPState state;
|
AsyncTCPState state;
|
||||||
uint32_t _timeout_ms;
|
uint32_t _timeout_ms;
|
||||||
String remota_addr; // address in numerical format (after DNS resolution), either IPv4 or IPv6
|
|
||||||
uint16_t remote_port; // remote port number
|
|
||||||
IPAddress local_addr;
|
IPAddress local_addr;
|
||||||
int32_t local_port; // -1 if unknown or invalid
|
int32_t local_port; // -1 if unknown or invalid
|
||||||
};
|
};
|
||||||
|
|
|
@ -24,6 +24,176 @@
|
||||||
|
|
||||||
#include <berry.h>
|
#include <berry.h>
|
||||||
|
|
||||||
|
// WiFiServerAsync can return either `WiFiClient` (sync) or `AsyncTCPClient` (async)
|
||||||
|
//
|
||||||
|
class WiFiServerAsync {
|
||||||
|
protected:
|
||||||
|
int sockfd;
|
||||||
|
int _accepted_sockfd = -1;
|
||||||
|
IPAddress _addr;
|
||||||
|
uint16_t _port;
|
||||||
|
uint8_t _max_clients;
|
||||||
|
bool _listening;
|
||||||
|
bool _noDelay = false;
|
||||||
|
|
||||||
|
|
||||||
|
public:
|
||||||
|
void listenOnLocalhost(){}
|
||||||
|
|
||||||
|
WiFiServerAsync(uint16_t port=80, uint8_t max_clients=4):sockfd(-1),_accepted_sockfd(-1),_addr(),_port(port),_max_clients(max_clients),_listening(false),_noDelay(false) {
|
||||||
|
}
|
||||||
|
WiFiServerAsync(const IPAddress& addr, uint16_t port=80, uint8_t max_clients=4):sockfd(-1),_accepted_sockfd(-1),_addr(addr),_port(port),_max_clients(max_clients),_listening(false),_noDelay(false) {
|
||||||
|
}
|
||||||
|
~WiFiServerAsync(){ end();}
|
||||||
|
WiFiClient available();
|
||||||
|
WiFiClient accept(){return available();}
|
||||||
|
AsyncTCPClient * availableAsync();
|
||||||
|
AsyncTCPClient * acceptAsync(){return availableAsync();}
|
||||||
|
void begin(uint16_t port=0);
|
||||||
|
void begin(uint16_t port, int reuse_enable);
|
||||||
|
void setNoDelay(bool nodelay);
|
||||||
|
bool getNoDelay();
|
||||||
|
bool hasClient();
|
||||||
|
|
||||||
|
void end();
|
||||||
|
void close();
|
||||||
|
void stop();
|
||||||
|
operator bool(){return _listening;}
|
||||||
|
int setTimeout(uint32_t seconds);
|
||||||
|
void stopAll(); public:
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
int WiFiServerAsync::setTimeout(uint32_t seconds){
|
||||||
|
struct timeval tv;
|
||||||
|
tv.tv_sec = seconds;
|
||||||
|
tv.tv_usec = 0;
|
||||||
|
if(setsockopt(sockfd, SOL_SOCKET, SO_RCVTIMEO, (char *)&tv, sizeof(struct timeval)) < 0)
|
||||||
|
return -1;
|
||||||
|
return setsockopt(sockfd, SOL_SOCKET, SO_SNDTIMEO, (char *)&tv, sizeof(struct timeval));
|
||||||
|
}
|
||||||
|
|
||||||
|
void WiFiServerAsync::stopAll(){}
|
||||||
|
|
||||||
|
WiFiClient WiFiServerAsync::available(){
|
||||||
|
if(!_listening)
|
||||||
|
return WiFiClient();
|
||||||
|
int client_sock;
|
||||||
|
if (_accepted_sockfd >= 0) {
|
||||||
|
client_sock = _accepted_sockfd;
|
||||||
|
_accepted_sockfd = -1;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
struct sockaddr_in6 _client;
|
||||||
|
int cs = sizeof(struct sockaddr_in6);
|
||||||
|
client_sock = lwip_accept(sockfd, (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) {
|
||||||
|
val = _noDelay;
|
||||||
|
if(setsockopt(client_sock, IPPROTO_TCP, TCP_NODELAY, (char*)&val, sizeof(int)) == ESP_OK)
|
||||||
|
return WiFiClient(client_sock);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return WiFiClient();
|
||||||
|
}
|
||||||
|
|
||||||
|
// specific Async version
|
||||||
|
AsyncTCPClient * WiFiServerAsync::availableAsync(){
|
||||||
|
if(!_listening)
|
||||||
|
return new AsyncTCPClient();
|
||||||
|
int client_sock;
|
||||||
|
if (_accepted_sockfd >= 0) {
|
||||||
|
client_sock = _accepted_sockfd;
|
||||||
|
_accepted_sockfd = -1;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
struct sockaddr_in6 _client;
|
||||||
|
int cs = sizeof(struct sockaddr_in6);
|
||||||
|
client_sock = lwip_accept(sockfd, (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) {
|
||||||
|
val = _noDelay;
|
||||||
|
if(setsockopt(client_sock, IPPROTO_TCP, TCP_NODELAY, (char*)&val, sizeof(int)) == ESP_OK)
|
||||||
|
return new AsyncTCPClient(client_sock);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return new AsyncTCPClient();
|
||||||
|
}
|
||||||
|
|
||||||
|
void WiFiServerAsync::begin(uint16_t port){
|
||||||
|
begin(port, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
void WiFiServerAsync::begin(uint16_t port, int enable){
|
||||||
|
if(_listening)
|
||||||
|
return;
|
||||||
|
if(port){
|
||||||
|
_port = port;
|
||||||
|
}
|
||||||
|
struct sockaddr_in6 server;
|
||||||
|
sockfd = socket(AF_INET6 , SOCK_STREAM, 0);
|
||||||
|
if (sockfd < 0)
|
||||||
|
return;
|
||||||
|
setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &enable, sizeof(int));
|
||||||
|
server.sin6_family = AF_INET6;
|
||||||
|
if (_addr.type() == IPv4) {
|
||||||
|
memcpy(server.sin6_addr.s6_addr+11, (uint8_t*)&_addr[0], 4);
|
||||||
|
server.sin6_addr.s6_addr[10] = 0xFF;
|
||||||
|
server.sin6_addr.s6_addr[11] = 0xFF;
|
||||||
|
} else {
|
||||||
|
memcpy(server.sin6_addr.s6_addr, (uint8_t*)&_addr[0], 16);
|
||||||
|
}
|
||||||
|
memset(server.sin6_addr.s6_addr, 0x0, 16);
|
||||||
|
server.sin6_port = htons(_port);
|
||||||
|
if(bind(sockfd, (struct sockaddr *)&server, sizeof(server)) < 0)
|
||||||
|
return;
|
||||||
|
if(listen(sockfd , _max_clients) < 0)
|
||||||
|
return;
|
||||||
|
fcntl(sockfd, F_SETFL, O_NONBLOCK);
|
||||||
|
_listening = true;
|
||||||
|
_noDelay = false;
|
||||||
|
_accepted_sockfd = -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
void WiFiServerAsync::setNoDelay(bool nodelay) {
|
||||||
|
_noDelay = nodelay;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool WiFiServerAsync::getNoDelay() {
|
||||||
|
return _noDelay;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool WiFiServerAsync::hasClient() {
|
||||||
|
if (_accepted_sockfd >= 0) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
struct sockaddr_in6 _client;
|
||||||
|
int cs = sizeof(struct sockaddr_in6);
|
||||||
|
_accepted_sockfd = lwip_accept(sockfd, (struct sockaddr *)&_client, (socklen_t*)&cs);
|
||||||
|
if (_accepted_sockfd >= 0) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void WiFiServerAsync::end(){
|
||||||
|
lwip_close(sockfd);
|
||||||
|
sockfd = -1;
|
||||||
|
_listening = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void WiFiServerAsync::close(){
|
||||||
|
end();
|
||||||
|
}
|
||||||
|
|
||||||
|
void WiFiServerAsync::stop(){
|
||||||
|
end();
|
||||||
|
}
|
||||||
|
|
||||||
/*********************************************************************************************\
|
/*********************************************************************************************\
|
||||||
* Native functions mapped to Berry functions
|
* Native functions mapped to Berry functions
|
||||||
*
|
*
|
||||||
|
@ -31,7 +201,7 @@
|
||||||
extern "C" {
|
extern "C" {
|
||||||
const void* tcpserver_init(struct bvm *vm, int32_t tcp_port) {
|
const void* tcpserver_init(struct bvm *vm, int32_t tcp_port) {
|
||||||
if (tcp_port > 0 && tcp_port < 65535) {
|
if (tcp_port > 0 && tcp_port < 65535) {
|
||||||
WiFiServer *server_tcp = new WiFiServer(tcp_port);
|
WiFiServerAsync *server_tcp = new WiFiServerAsync(tcp_port);
|
||||||
server_tcp->begin(); // start TCP server
|
server_tcp->begin(); // start TCP server
|
||||||
if (!*server_tcp) {
|
if (!*server_tcp) {
|
||||||
be_raise(vm, "network_error", "Failed to open socket");
|
be_raise(vm, "network_error", "Failed to open socket");
|
||||||
|
@ -45,7 +215,7 @@ extern "C" {
|
||||||
}
|
}
|
||||||
|
|
||||||
void tcpserver_deinit(void *server) {
|
void tcpserver_deinit(void *server) {
|
||||||
WiFiServer *server_tcp = (WiFiServer*) server;
|
WiFiServerAsync *server_tcp = (WiFiServerAsync*) server;
|
||||||
if (server_tcp) {
|
if (server_tcp) {
|
||||||
server_tcp->stop();
|
server_tcp->stop();
|
||||||
delete server_tcp;
|
delete server_tcp;
|
||||||
|
@ -53,23 +223,23 @@ extern "C" {
|
||||||
}
|
}
|
||||||
|
|
||||||
void tcpserver_close(void *server) {
|
void tcpserver_close(void *server) {
|
||||||
WiFiServer *server_tcp = (WiFiServer*) server;
|
WiFiServerAsync *server_tcp = (WiFiServerAsync*) server;
|
||||||
if (server_tcp) {
|
if (server_tcp) {
|
||||||
server_tcp->stop();
|
server_tcp->stop();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bbool tcpserver_hasclient(void *server) {
|
bbool tcpserver_hasclient(void *server) {
|
||||||
WiFiServer *server_tcp = (WiFiServer*) server;
|
WiFiServerAsync *server_tcp = (WiFiServerAsync*) server;
|
||||||
return server_tcp->hasClient();
|
return server_tcp->hasClient();
|
||||||
}
|
}
|
||||||
|
|
||||||
void * tcpserver_accept(struct bvm *vm, void *server) {
|
void * tcpserver_accept(struct bvm *vm, void *server) {
|
||||||
WiFiServer *server_tcp = (WiFiServer*) server;
|
WiFiServerAsync *server_tcp = (WiFiServerAsync*) server;
|
||||||
WiFiClient * client_ptr = nullptr;
|
WiFiClient * client_ptr = nullptr;
|
||||||
if (server_tcp->hasClient()) {
|
if (server_tcp->hasClient()) {
|
||||||
WiFiClient new_client = server_tcp->available();
|
WiFiClient new_client = server_tcp->available();
|
||||||
AddLog(LOG_LEVEL_INFO, "BRY: Got connection from %s", new_client.remoteIP().toString().c_str());
|
AddLog(LOG_LEVEL_INFO, "BRY: Got connection from %s:%i local %s:%i", new_client.remoteIP().toString().c_str(), new_client.remotePort(), new_client.localIP().toString().c_str(), new_client.localPort());
|
||||||
client_ptr = new WiFiClient();
|
client_ptr = new WiFiClient();
|
||||||
*client_ptr = new_client;
|
*client_ptr = new_client;
|
||||||
} else {
|
} else {
|
||||||
|
@ -78,6 +248,18 @@ extern "C" {
|
||||||
return client_ptr;
|
return client_ptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void * tcpserver_acceptasync(struct bvm *vm, void *server) {
|
||||||
|
WiFiServerAsync *server_tcp = (WiFiServerAsync*) server;
|
||||||
|
AsyncTCPClient * client_ptr = nullptr;
|
||||||
|
if (server_tcp->hasClient()) {
|
||||||
|
client_ptr = server_tcp->availableAsync();
|
||||||
|
AddLog(LOG_LEVEL_INFO, "BRY: Got connection from %s:%i local %s:%i", client_ptr->remoteIP().toString().c_str(), client_ptr->remotePort(), client_ptr->localIP().toString().c_str(), client_ptr->localPort());
|
||||||
|
} else {
|
||||||
|
be_raise(vm, "internal_error", "tcpserver has no client connected");
|
||||||
|
}
|
||||||
|
return client_ptr;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif // USE_BERRY_TCPSERVER
|
#endif // USE_BERRY_TCPSERVER
|
||||||
|
|
Loading…
Reference in New Issue