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

@@ -92,7 +92,7 @@ set(PIP_UTILS_LIST)
set(PIP_TESTS_LIST) set(PIP_TESTS_LIST)
set(PIP_EXPORTS) set(PIP_EXPORTS)
set(PIP_SRC_MODULES "console;crypt;compress;usb;fftw;opencl;io_utils;cloud;lua") set(PIP_SRC_MODULES "console;crypt;compress;usb;fftw;opencl;io_utils;client_server;cloud;lua")
foreach(_m ${PIP_SRC_MODULES}) foreach(_m ${PIP_SRC_MODULES})
set(PIP_MSG_${_m} "no") set(PIP_MSG_${_m} "no")
endforeach() endforeach()
@@ -392,6 +392,7 @@ if (NOT CROSSTOOLS)
pip_find_lib(sodium) pip_find_lib(sodium)
if(sodium_FOUND) if(sodium_FOUND)
pip_module(crypt "sodium" "PIP crypt support" "" "" "") pip_module(crypt "sodium" "PIP crypt support" "" "" "")
pip_module(client_server "pip_io_utils" "PIP client-server helper" "" "" "")
pip_module(cloud "pip_io_utils" "PIP cloud support" "" "" "") pip_module(cloud "pip_io_utils" "PIP cloud support" "" "" "")
endif() endif()
@@ -480,7 +481,7 @@ if (NOT CROSSTOOLS)
#target_link_libraries(pip_plugin pip) #target_link_libraries(pip_plugin pip)
add_executable(pip_test "main.cpp") add_executable(pip_test "main.cpp")
target_link_libraries(pip_test pip pip_io_utils) target_link_libraries(pip_test pip pip_io_utils pip_client_server)
if(sodium_FOUND) if(sodium_FOUND)
add_executable(pip_cloud_test "main_picloud_test.cpp") add_executable(pip_cloud_test "main_picloud_test.cpp")
target_link_libraries(pip_cloud_test pip_cloud) target_link_libraries(pip_cloud_test pip_cloud)
@@ -585,7 +586,7 @@ if ((NOT PIP_FREERTOS) AND (NOT CROSSTOOLS))
find_package(Doxygen) find_package(Doxygen)
if(DOXYGEN_FOUND) if(DOXYGEN_FOUND)
set(DOXY_DEFINES "${PIP_EXPORTS}") set(DOXY_DEFINES "${PIP_EXPORTS}")
foreach (_m "console" "usb" "compress" "crypt" "cloud" "fftw" "opencl" "io_utils" "lua") foreach (_m "console" "usb" "compress" "crypt" "client_server" "cloud" "fftw" "opencl" "io_utils" "lua")
string(TOUPPER "${_m}" _mdef) string(TOUPPER "${_m}" _mdef)
list(APPEND DOXY_DEFINES "PIP_${_mdef}_EXPORT") list(APPEND DOXY_DEFINES "PIP_${_mdef}_EXPORT")
endforeach() endforeach()

View File

@@ -9,6 +9,7 @@ Create imported targets:
* PIP::FFTW * PIP::FFTW
* PIP::OpenCL * PIP::OpenCL
* PIP::IOUtils * PIP::IOUtils
* PIP::ClientServer
* PIP::Cloud * PIP::Cloud
* PIP::Lua * PIP::Lua
@@ -22,7 +23,7 @@ include(SHSTKMacros)
shstk_set_find_dirs(PIP) shstk_set_find_dirs(PIP)
set(__libs "usb;crypt;console;fftw;compress;io_utils;opencl;cloud;lua") set(__libs "usb;crypt;console;fftw;compress;opencl;io_utils;client_server;cloud;lua")
if (BUILDING_PIP) if (BUILDING_PIP)
#set(_libs "pip;pip_usb;pip_console;pip_crypt;pip_fftw;pip_compress;pip_opencl;pip_io_utils;pip_cloud;pip_lua") #set(_libs "pip;pip_usb;pip_console;pip_crypt;pip_fftw;pip_compress;pip_opencl;pip_io_utils;pip_cloud;pip_lua")
@@ -83,15 +84,16 @@ if(PIP_FIND_VERSION VERSION_GREATER PIP_VERSION)
message(FATAL_ERROR "PIP version ${PIP_VERSION} is available, but ${PIP_FIND_VERSION} requested!") message(FATAL_ERROR "PIP version ${PIP_VERSION} is available, but ${PIP_FIND_VERSION} requested!")
endif() endif()
set(__module_usb USB ) set(__module_usb USB )
set(__module_console Console ) set(__module_console Console )
set(__module_crypt Crypt ) set(__module_crypt Crypt )
set(__module_fftw FFTW ) set(__module_fftw FFTW )
set(__module_compress Compress ) set(__module_compress Compress )
set(__module_opencl OpenCL ) set(__module_opencl OpenCL )
set(__module_io_utils IOUtils ) set(__module_io_utils IOUtils )
set(__module_cloud Cloud ) set(__module_client_server ClientServer)
set(__module_lua Lua ) set(__module_cloud Cloud )
set(__module_lua Lua )
foreach (_l ${__libs}) foreach (_l ${__libs})
set( __inc_${_l} "") set( __inc_${_l} "")
@@ -99,8 +101,9 @@ foreach (_l ${__libs})
set(__libs_${_l} "") set(__libs_${_l} "")
endforeach() endforeach()
set(__deps_io_utils "PIP::Crypt") set(__deps_io_utils "PIP::Crypt" )
set(__deps_cloud "PIP::IOUtils") set(__deps_client_server "PIP::IOUtils")
set(__deps_cloud "PIP::IOUtils")
if (BUILDING_PIP) if (BUILDING_PIP)

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 "pithread.h"
#include "pitime.h" #include "pitime.h"
#define WAIT_FOR_EXIT \ #define WAIT_FOR_EXIT \
while (!PIKbdListener::exiting) \ while (!PIKbdListener::exiting) \
piMSleep(PIP_MIN_MSLEEP * 5); // TODO: rewrite with condvar piMSleep(PIP_MIN_MSLEEP * 5); \
if (PIKbdListener::instance()) PIKbdListener::instance()->stopAndWait();
class PIP_EXPORT PIKbdListener: public PIThread { 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); closeSocket(sock);
init(); init();
disconnected(true); disconnected(true);
if (params[KeepConnection]) {
connect();
}
} }
}; };
if (!isOptionSet(BlockingWrite)) { if (!isOptionSet(BlockingWrite)) {

View File

@@ -1,4 +1,6 @@
#include "pibytearray.h" #include "pibytearray.h"
#include "piclient_server_client.h"
#include "piclient_server_server.h"
#include "picodeparser.h" #include "picodeparser.h"
#include "piiostream.h" #include "piiostream.h"
#include "pijson.h" #include "pijson.h"
@@ -16,22 +18,51 @@ enum MyEvent {
meIntString, meIntString,
}; };
PITimeMeasurer tm; PIKbdListener kbd;
std::atomic_int cnt = {0};
void tfunc(int delim) {
// piCout << "tick with delimiter" << delim; class MyClient: public PIClientServer::Client {
++cnt; protected:
}; void readed(PIByteArray data) override { piCout << "readed" << (data.size()); }
void tfunc4(int delim) { void connected() override {
piCout << "tick4 with delimiter" << delim; send_thread.start(
[this] {
// write((PIString::fromNumber(++counter)).toUTF8());
PIByteArray ba(64_MiB);
write(ba);
},
2_Hz);
}
PIThread send_thread;
int counter = 0;
}; };
int main(int argc, char * argv[]) { int main(int argc, char * argv[]) {
uint v = 0xaabbccdd; kbd.enableExitCapture();
piCout << Hex << v << piChangedEndian(v);
piChangeEndianBinary(&v, sizeof(v)); piCout << argc;
piCout << Hex << v << piChangedEndian(v);
PIClientServer::Server * s = nullptr;
PIClientServer::Client * c = nullptr;
if (argc > 1) {
// server
s = new PIClientServer::Server();
s->setClientFactory([] { return new MyClient(); });
s->listenAll(12345);
} else {
// client
c = new MyClient();
c->createNew();
c->connect(PINetworkAddress::resolve("127.0.0.1", 12345));
}
WAIT_FOR_EXIT;
piDeleteSafety(s);
piDeleteSafety(c);
return 0; return 0;
/*PIPackedTCP * tcp_s = /*PIPackedTCP * tcp_s =
PIIODevice::createFromFullPath("ptcp://s::8000")->cast<PIPackedTCP>(); // new PIPackedTCP(PIPackedTCP::Server, {"0.0.0.0:8000"}); PIIODevice::createFromFullPath("ptcp://s::8000")->cast<PIPackedTCP>(); // new PIPackedTCP(PIPackedTCP::Server, {"0.0.0.0:8000"});