first try, works

This commit is contained in:
2024-09-11 10:18:45 +03:00
parent 16a818c95e
commit 0d94699206
9 changed files with 441 additions and 30 deletions

View File

@@ -0,0 +1,116 @@
/*
PIP - Platform Independent Primitives
Ivan Pelipenko peri4ko@yandex.ru
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "piclient_server_client.h"
#include "piethernet.h"
#include "piliterals_time.h"
#include "pitime.h"
PIClientServer::Client::Client() {}
PIClientServer::Client::~Client() {
stop();
if (own_tcp) piDeleteSafety(tcp);
}
void PIClientServer::Client::createNew() {
if (tcp) return;
tcp = new PIEthernet(PIEthernet::TCP_Client);
tcp->setParameter(PIEthernet::KeepConnection, true);
own_tcp = true;
init();
}
void PIClientServer::Client::connect(PINetworkAddress addr) {
if (!tcp || !own_tcp) return;
stop();
tcp->connect(addr, true);
tcp->startThreadedRead();
piCout << "Connect to" << addr.toString();
}
void PIClientServer::Client::stop() {
if (!tcp) return;
can_write = false;
tcp->interrupt();
tcp->stopAndWait(10_s);
if (tcp->isThreadedRead()) tcp->terminateThreadedRead();
stream.clear();
}
int PIClientServer::Client::write(const void * d, const size_t s) {
if (!tcp) return -1;
if (!can_write) return 0;
PIMutexLocker guard(write_mutex);
piCout << "... send ...";
stream.send(PIByteArray(d, s));
piCout << "... send ok";
return s;
}
void PIClientServer::Client::createForServer(PIEthernet * tcp_) {
tcp = tcp_;
tcp->setParameter(PIEthernet::KeepConnection, false);
init();
}
void PIClientServer::Client::init() {
if (!tcp) return;
CONNECTL(&stream, sendRequest, [this](const PIByteArray & ba) {
if (!can_write) return;
tcp->send(ba);
// piMSleep(1);
});
CONNECTL(&stream, packetReceiveEvent, [this](PIByteArray & ba) {
if (readed_func) readed_func(ba);
readed(ba);
});
CONNECTL(tcp, threadedReadEvent, [this](const uchar * readed, ssize_t size) {
if (!can_write) return;
stream.received(readed, size);
});
CONNECTL(tcp, connected, [this]() {
can_write = true;
piCout << "Connected";
connected();
});
CONNECTL(tcp, disconnected, [this](bool) {
can_write = false;
stream.clear();
piCout << "Disconnected";
disconnected();
});
}
void PIClientServer::Client::destroy() {
can_write = false;
write_mutex.lock();
piDeleteSafety(tcp);
aboutDelete();
piCout << "Destroyed";
}

View File

@@ -0,0 +1,106 @@
/*
PIP - Platform Independent Primitives
Ivan Pelipenko peri4ko@yandex.ru
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "piclient_server_server.h"
#include "piclient_server_client.h"
#include "piethernet.h"
#include "piliterals_time.h"
PIClientServer::Server::Server() {
tcp_server = new PIEthernet(PIEthernet::TCP_Server);
clean_thread = new PIThread();
client_factory = [] { return new Client(); };
CONNECTL(tcp_server, newConnection, [this](PIEthernet * c) {
PIMutexLocker guard(clients_mutex);
if (clients.size_s() >= max_clients) {
piCout << "Server::newConnection overflow clients count";
delete c;
return;
}
auto sc = client_factory();
if (!sc) {
piCout << "ClientFactory returns nullptr!";
return;
}
sc->createForServer(c);
newClient(sc);
});
clean_thread->start(
[this]() {
PIVector<Client *> to_delete;
clients_mutex.lock();
for (auto c: clients) {
const PIEthernet * eth = c->getTCP();
if (!eth) continue;
if (eth->isConnected()) continue;
c->can_write = false;
to_delete << c;
}
for (auto c: to_delete)
clients.removeOne(c);
clients_mutex.unlock();
for (auto c: to_delete) {
c->destroy();
delete c;
}
},
5_Hz);
}
PIClientServer::Server::~Server() {
clean_thread->stopAndWait();
piDeleteSafety(clean_thread);
stopServer();
for (auto c: clients) {
c->destroy();
delete c;
}
piDeleteSafety(tcp_server);
}
void PIClientServer::Server::listen(PINetworkAddress addr) {
if (!tcp_server) return;
stopServer();
tcp_server->listen(addr, true);
// piCout << "Listen on" << addr.toString();
}
void PIClientServer::Server::setMaxClients(int new_max_clients) {
max_clients = new_max_clients;
}
void PIClientServer::Server::stopServer() {
if (!tcp_server) return;
tcp_server->stopAndWait();
}
void PIClientServer::Server::newClient(Client * c) {
c->readed_func = [this, c](PIByteArray ba) { readed(c, ba); };
clients << c;
c->tcp->startThreadedRead();
c->connected();
piCout << "New client";
}

View File

@@ -0,0 +1,78 @@
/*! \file piclient_server_client.h
* \ingroup ClientServer
* \~\brief
* \~english
* \~russian
*/
/*
PIP - Platform Independent Primitives
Ivan Pelipenko peri4ko@yandex.ru
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef piclient_server_client_H
#define piclient_server_client_H
#include "pinetworkaddress.h"
#include "pip_client_server_export.h"
#include "pistreampacker.h"
class PIEthernet;
namespace PIClientServer {
class Server;
class PIP_CLIENT_SERVER_EXPORT Client: public PIObject {
friend class Server;
NO_COPY_CLASS(Client);
PIOBJECT(Client);
public:
Client();
virtual ~Client();
void createNew();
void connect(PINetworkAddress addr);
const PIEthernet * getTCP() const { return tcp; }
void stop();
int write(const void * d, const size_t s);
int write(const PIByteArray & ba) { return write(ba.data(), ba.size()); }
protected:
virtual void readed(PIByteArray data) {}
virtual void connected() {}
virtual void disconnected() {}
virtual void aboutDelete() {}
private:
void createForServer(PIEthernet * tcp_);
void init();
void destroy();
bool own_tcp = false;
std::atomic_bool can_write = {true};
PIEthernet * tcp = nullptr;
PIStreamPacker stream;
mutable PIMutex write_mutex;
std::function<void(PIByteArray data)> readed_func = nullptr;
};
} // namespace PIClientServer
#endif

View File

@@ -0,0 +1,72 @@
/*! \file piclient_server_server.h
* \ingroup ClientServer
* \~\brief
* \~english
* \~russian
*/
/*
PIP - Platform Independent Primitives
Ivan Pelipenko peri4ko@yandex.ru
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef piclient_server_server_H
#define piclient_server_server_H
#include "pimutex.h"
#include "pinetworkaddress.h"
#include "pip_client_server_export.h"
class PIEthernet;
class PIThread;
namespace PIClientServer {
class Client;
class PIP_CLIENT_SERVER_EXPORT Server {
public:
Server();
virtual ~Server();
void listen(PINetworkAddress addr);
void listenAll(ushort port) { listen({0, port}); }
int getMaxClients() const { return max_clients; }
void setMaxClients(int new_max_clients);
void setClientFactory(std::function<Client *()> f) { client_factory = f; }
void write(Client * c, const PIByteArray & data);
protected:
virtual void readed(Client * c, PIByteArray data) {}
private:
void stopServer();
void newClient(Client * c);
std::function<Client *()> client_factory;
PIEthernet * tcp_server = nullptr;
PIThread * clean_thread = nullptr;
PIVector<Client *> clients;
PIMutex clients_mutex;
int max_clients = 1000;
};
} // namespace PIClientServer
#endif

View File

@@ -29,9 +29,10 @@
#include "pithread.h"
#include "pitime.h"
#define WAIT_FOR_EXIT \
while (!PIKbdListener::exiting) \
piMSleep(PIP_MIN_MSLEEP * 5); // TODO: rewrite with condvar
#define WAIT_FOR_EXIT \
while (!PIKbdListener::exiting) \
piMSleep(PIP_MIN_MSLEEP * 5); \
if (PIKbdListener::instance()) PIKbdListener::instance()->stopAndWait();
class PIP_EXPORT PIKbdListener: public PIThread {

View File

@@ -777,6 +777,9 @@ ssize_t PIEthernet::writeDevice(const void * data, ssize_t max_size) {
closeSocket(sock);
init();
disconnected(true);
if (params[KeepConnection]) {
connect();
}
}
};
if (!isOptionSet(BlockingWrite)) {