KEK/comm_tcp_socket_client.cpp
Folkert van Heusden 2d9f953959
sleep while locked
2024-06-26 20:41:44 +02:00

205 lines
4.2 KiB
C++

// (C) 2024 by Folkert van Heusden
// Released under MIT license
#include "gen.h"
#if defined(ESP32)
#include <Arduino.h>
#endif
#if defined(ESP32)
#include <lwip/netdb.h>
#include <lwip/sockets.h>
#include <arpa/inet.h>
#include <sys/socket.h>
#include <driver/uart.h>
#elif defined(_WIN32)
// from https://stackoverflow.com/questions/12765743/implicit-declaration-of-function-getaddrinfo-on-mingw
#define _NTDDI_VERSION_FROM_WIN32_WINNT2(ver) ver##0000
#define _NTDDI_VERSION_FROM_WIN32_WINNT(ver) _NTDDI_VERSION_FROM_WIN32_WINNT2(ver)
#ifndef _WIN32_WINNT
# define _WIN32_WINNT 0x501
#endif
#ifndef NTDDI_VERSION
# define NTDDI_VERSION _NTDDI_VERSION_FROM_WIN32_WINNT(_WIN32_WINNT)
#endif
#include <ws2tcpip.h>
#include <winsock2.h>
#else
#include <poll.h>
#include <netdb.h>
#include <arpa/inet.h>
#include <sys/socket.h>
#endif
#include <cstring>
#if IS_POSIX
#include <errno.h>
#include <fcntl.h>
#include <thread>
#include <unistd.h>
#endif
#include "comm_tcp_socket_client.h"
#include "log.h"
#include "utils.h"
comm_tcp_socket_client::comm_tcp_socket_client(const std::string & host, const int port) :
host(host),
port(port)
{
}
comm_tcp_socket_client::~comm_tcp_socket_client()
{
stop_flag = true;
if (th) {
th->join();
delete th;
}
}
bool comm_tcp_socket_client::begin()
{
th = new std::thread(std::ref(*this));
return true;
}
bool comm_tcp_socket_client::is_connected()
{
std::unique_lock<std::mutex> lck(cfd_lock);
return cfd != INVALID_SOCKET;
}
bool comm_tcp_socket_client::has_data()
{
std::unique_lock<std::mutex> lck(cfd_lock);
#if defined(_WIN32)
WSAPOLLFD fds[] { { cfd, POLLIN, 0 } };
int rc = WSAPoll(fds, 1, 0);
#else
pollfd fds[] { { cfd, POLLIN, 0 } };
int rc = poll(fds, 1, 0);
#endif
return rc == 1;
}
uint8_t comm_tcp_socket_client::get_byte()
{
int use_fd = -1;
{
std::unique_lock<std::mutex> lck(cfd_lock);
use_fd = cfd;
}
uint8_t c = 0;
if (read(use_fd, &c, 1) <= 0) {
DOLOG(warning, false, "comm_tcp_socket_client::get_byte: failed");
std::unique_lock<std::mutex> lck(cfd_lock);
close(cfd);
cfd = INVALID_SOCKET;
}
return c;
}
void comm_tcp_socket_client::send_data(const uint8_t *const in, const size_t n)
{
const uint8_t *p = in;
size_t len = n;
while(len > 0) {
std::unique_lock<std::mutex> lck(cfd_lock);
int rc = write(cfd, p, len);
if (rc <= 0) { // TODO error checking
DOLOG(warning, false, "comm_tcp_socket_client::send_data: failed");
close(cfd);
cfd = INVALID_SOCKET;
break;
}
p += rc;
len -= rc;
}
}
void comm_tcp_socket_client::operator()()
{
set_thread_name("kek:COMMTCPC");
DOLOG(info, true, "TCP comm (client) thread started for %s:%d", host.c_str(), port);
while(!stop_flag) {
myusleep(101000l);
std::unique_lock<std::mutex> lck(cfd_lock);
if (cfd != INVALID_SOCKET)
continue;
addrinfo *res = nullptr;
addrinfo hints { };
hints.ai_family = AF_INET;
hints.ai_socktype = SOCK_STREAM;
char port_str[8] { 0 };
snprintf(port_str, sizeof port_str, "%u", port);
int rc = getaddrinfo(host.c_str(), port_str, &hints, &res);
if (rc != 0) {
#ifdef ESP32
DOLOG(ll_error, true, "comm_tcp_socket_client: cannot resolve \"%s\":%s", host.c_str(), port_str);
#else
DOLOG(ll_error, true, "comm_tcp_socket_client: cannot resolve \"%s\":%s: %s", host.c_str(), port_str, gai_strerror(rc));
#endif
continue;
}
for(addrinfo *p = res; p != NULL; p = p->ai_next) {
if ((cfd = socket(p->ai_family, p->ai_socktype, p->ai_protocol)) == -1)
continue;
if (::connect(cfd, p->ai_addr, p->ai_addrlen) == -1) {
close(cfd);
cfd = INVALID_SOCKET;
DOLOG(ll_error, true, "comm_tcp_socket_client: cannot connect");
continue;
}
break;
}
freeaddrinfo(res);
if (cfd != INVALID_SOCKET)
set_nodelay(cfd);
}
DOLOG(info, true, "comm_tcp_socket_client thread terminating");
close(cfd);
}
JsonDocument comm_tcp_socket_client::serialize() const
{
JsonDocument j;
j["comm-backend-type"] = "tcp-client";
j["host"] = host;
j["port"] = port;
return j;
}
comm_tcp_socket_client *comm_tcp_socket_client::deserialize(const JsonVariantConst j)
{
comm_tcp_socket_client *r = new comm_tcp_socket_client(j["host"].as<std::string>(), j["port"].as<int>());
return r;
}