socket/src/tcp_socket.cpp
2023-09-27 21:00:30 +02:00

520 lines
13 KiB
C++

#include "../lib/tcp_socket.hpp"
/**
* Kontrustruktor varijable tipa server, prima port i limit za ograničenje liste klijenata na čekanju
*/
server::server (const ushort port, const uint queue, SSL_CTX* _securefds) {
securefds = _securefds;
addr.sin_family = AF_INET;
addr.sin_addr.s_addr = INADDR_ANY;
addr.sin_port = htons(port);
#if _WIN32
if (WSAStartup(MAKEWORD(2,2),&wsa) != 0) {
throw string("[ERROR] WSA Startup. Detail: " + to_string(WSAGetLastError()));
}
#endif
sock = socket(AF_INET, SOCK_STREAM, 0);
if (sock <= 0) {
throw string("[ERROR] Unable to open TCP socket ");
}
int opt=1;
#if __linux__
if (setsockopt(sock, SOL_SOCKET, SO_REUSEADDR | SO_REUSEPORT, &opt, sizeof(opt))) {
throw string("[ERROR] Unable to set REUSEADDR or REUSEPORT on socket ");
}
#elif _WIN32
if (setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, (const char*)&opt, sizeof(opt))) {
throw string("[ERROR] Unable to set REUSEADDR or REUSEPORT on socket ");
}
#endif
if (bind(sock, (struct sockaddr *)&addr, sizeof(struct sockaddr_in)) < 0) {
throw string("[ERROR] Unable to bind socket ");
}
if (listen(sock, queue) < 0) {
throw string("[ERROR] It is not possible to set the allowed number of waiting clients ");
}
}
/**
* Metoda za sinkroni rad s klijentima, prima pokazivač na funkciju i timeout;
* Funkcija handlecli prima referencu tipa client - važno za definiranje funkcija koje se šalju;
* Nije moguće proslijediti druge parametre;
*/
void server::sync(void (*handlecli)(client&), const uint timeout) {
do {
client cli(this, timeout, securefds);
handlecli(cli);
} while (true);
}
/**
* Metoda za asinkdorni rad s klijentima, prima limit, pokazivač na funkciju i timeout;
* Funkcija handlecli prima referencu tipa client - važno za definiranje funkcija koje se šalju;
* Nije moguće proslijediti druge parametre;
*/
void server::async(const uint limit, void (*handlecli)(client&, mutex&), const uint timeout) {
mutex io;
do {
for (uint i=0; i<limit; i++) {
thr.push_back(thread([&](){
client cli(this, timeout, securefds);
handlecli(cli, io);
}));
}
for (uint i=0; i<limit; i++) {
thr[i].join();
}
thr.clear();
} while (true);
}
/**
* Destruktor varijable tipa server
*/
server::~server () {
if (sock<=0) {
throw string("[ERROR] The socket is already closed ");
}
else {
#if __linux__
if (close(sock) != 0) {
throw string("[ERROR] Unable to close socket ");
}
#elif _WIN32
if (closesocket(sock) != 0) {
throw string("[ERROR] Unable to close socket ");
}
WSACleanup();
#endif
}
}
/**
* Konstruktor varijable tipa secure za klijentske implementacije
*/
secure::secure() {
SSL_library_init();
SSL_load_error_strings();
OpenSSL_add_all_algorithms();
fds = SSL_CTX_new(SSLv23_client_method());
if (!fds) {
throw string("[ERROR] Creating SSL context ");
}
}
/**
* Konstruktor varijable tipa secure za serverske implementacije
*/
secure::secure(const string cert, const string priv) {
SSL_library_init();
SSL_load_error_strings();
OpenSSL_add_all_algorithms();
// Create an SSL context
fds = SSL_CTX_new(SSLv23_server_method());
if (!fds) {
throw string("[ERROR] Creating SSL context ");
}
// Load the server's certificate and private key files
if (SSL_CTX_use_certificate_file(fds, cert.c_str(), SSL_FILETYPE_PEM) <= 0) {
throw string("[ERROR] Loading certificate file ");
}
if (SSL_CTX_use_PrivateKey_file(fds, priv.c_str(), SSL_FILETYPE_PEM) <= 0) {
throw string("[ERROR] Loading private key file ");
}
}
/**
* Destruktor varijable tipa secure
*/
secure::~secure () {
SSL_CTX_free(fds);
}
/**
* Konstruktor varijable tipa client
* Prima adresu, port, i opcijonalno dozvoljeno vrijeme čekanja servera
* i deskriptor datoteke SSL certifikat za sigurne komunikacije
*/
client::client(const string address, const ushort port, const uint timeout, SSL_CTX* securefds) {
#if _WIN32
if (WSAStartup(MAKEWORD(2,2),&wsa) != 0) {
throw string("[ERROR] Unable to set WinSock " + to_string(WSAGetLastError()));
}
#endif
conn = socket(AF_INET, SOCK_STREAM, 0);
if (conn < 0) {
throw string("[ERROR] Unable to open TCP socket ");
}
const string _address = isIPAddress(address) ? address : ipFromDomain(address);
addr.sin_family = AF_INET;
addr.sin_addr.s_addr = inet_addr(_address.c_str());
addr.sin_port = htons(port);
if (connect(conn, (struct sockaddr*)&addr, sizeof(struct sockaddr_in)) != 0) {
throw string("Unable to connect to server ");
}
#if __linux__
struct timeval tv;
tv.tv_sec = timeout/1000;
tv.tv_usec = (timeout%1000)*1000;
if (setsockopt(conn, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(struct timeval))) {
throw string("[ERROR] Unable to set timeout ");
}
#elif _WIN32
DWORD tv = timeout;
if (setsockopt(conn, SOL_SOCKET, SO_RCVTIMEO, (const char*)&tv, sizeof(tv))) {
throw string("[ERROR] Unable to set timeout ");
}
#endif
if (securefds) {
ssl = SSL_new(securefds);
if (!ssl) {
throw string("[ERROR] Creating SSL object ");
}
SSL_set_fd(ssl, conn);
// Perform the SSL handshake
if (SSL_connect(ssl) <= 0) {
SSL_free(ssl);
throw string("[ERROR] Performing SSL handshake ");
}
}
}
/**
* Konstruktor varijable tipa commint
* Prima pokazivač na inicijaliziranu varijablu tipa, port,
* i opcijonalno dozvoljeno vrijeme čekanja servera i deskriptor datoteke
* SSL certifikat za sigurne komunikacije
*/
client::client(const server *_srv, const uint timeout, SSL_CTX* securefds) {
srv = _srv;
socklen_t len = sizeof(struct sockaddr_in);
if ((conn = accept(srv->sock, (struct sockaddr *)&(srv->addr), (socklen_t*)&len)) < 0) {
throw string("[ERROR] Unable to accept client connection ");
}
#if __linux__
struct timeval tv;
tv.tv_sec = timeout/1000;
tv.tv_usec = (timeout%1000)*1000;
if (setsockopt(conn, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(struct timeval))) {
throw string("[ERROR] Unable to set timeout ");
}
#elif _WIN32
DWORD tv = timeout;
if (setsockopt(conn, SOL_SOCKET, SO_RCVTIMEO, (const char*)&tv, sizeof(tv))) {
throw string("[ERROR] Unable to set timeout ");
}
#endif
if (securefds) {
ssl = SSL_new(securefds);
if (!ssl) {
throw string("[ERROR] Creating SSL object ");
}
SSL_set_fd(ssl, conn);
// Perform SSL handshake
if (SSL_accept(ssl) <= 0) {
SSL_free(ssl);
throw string("[ERROR] Performing SSL handshake ");
}
}
char ipv4_buff[INET_ADDRSTRLEN];
char ipv6_buff[INET6_ADDRSTRLEN];
inet_ntop(AF_INET, &(srv->addr.sin_addr), ipv4_buff, INET_ADDRSTRLEN);
ipv4 = ipv4_buff;
inet_ntop(AF_INET6, &(srv->addr.sin_addr), ipv6_buff, INET6_ADDRSTRLEN);
ipv6 = ipv6_buff;
}
/**
* Destruktor varijable tipa client
*/
client::~client () {
if (ssl) {
SSL_shutdown(ssl);
SSL_free(ssl);
}
if (conn <= 0) {
throw string("[ERROR] The socket is already closed ");
}
else {
#if __linux__
if (close(conn) != 0) {
throw string("[ERROR] Unable to close socket ");
}
#elif _WIN32
if (closesocket(conn) != 0) {
throw string("[ERROR] Unable to close socket ");
}
//WSACleanup();
#endif
}
}
/**
* Metoda klase client za slanje podataka preko soketa
* Prima string koji će biti poslan
* Vraća logički statu poređenja psolanih karaktera i karaktera u stringu
*/
bool client::push (const string msg) {
size_t sended = 0;
if (ssl) {
sended = SSL_write(ssl, msg.c_str(), msg.length());
}
else {
sended = send(conn, msg.c_str(), msg.length(), 0);
}
return sended == msg.length();
}
/**
* Metoda klase client za primanje poruke preko soketa
* Prima dozvoljeni broj karaktera koji će primiti
* Vraća string primljene poruke
*/
string client::pull (size_t byte_limit) {
char res[byte_limit] = {0};
if (ssl) {
SSL_read(ssl, res, byte_limit);
}
else {
recv(conn , res, byte_limit, 0);
}
return string(res);
}
queue::queue (const uint _limit, const string _address, const ushort _port, const uint _timeout, SSL_CTX* _securefds) {
if (_limit > 1) {
limit = _limit;
}
else {
throw string("[ERROR] Limit out of range ");
}
address = _address;
port = _port;
timeout = _timeout;
securefds = _securefds;
// for (uint i=0; i<limit; i++) {
// if (address.empty() && srv != NULL) {
// clients.push_back(new client(srv, timeout, securefds));
// }
// else {
// clients.push_back(new client(address, port, timeout, securefds));
// }
// }
// controller = thread([&](){
// cout << "Upao u tred "<< endl;
// while(true) {
// // io.lock();
// // uint missing = limit-clients.size();
// // cout << "nedostaje " << missing << endl;
// // if (missing > 0.6*limit) {
// // enqueue();
// if (clients.size() <= 1) {
// if (address.empty() && srv != NULL) {
// clients.push_back(new client(srv, timeout, securefds));
// }
// else {
// clients.push_back(new client(address, port, timeout, securefds));
// }
// }
// // }
// // usleep(missing*100000);
// // io.unlock();
// // usleep(1000);
// sleep(1);
// }
// });
}
queue::queue (const uint _limit, server *_srv, const uint _timeout, SSL_CTX* _securefds) {
if (_limit > 1) {
limit = _limit;
}
else {
throw string("[ERROR] Limit out of range ");
}
srv = _srv;
timeout = _timeout;
securefds = _securefds;
controller = thread([this](){
cout << "Upao u tred "<< endl;
while(true) {
io.lock();
// uint missing = limit-clients.size();
// cout << "nedostaje " << missing << endl;
// if (missing > 0.6*limit) {
// enqueue();
if (clients.size()< limit) {
if (address.empty() && srv != NULL) {
clients.push_back(new client(srv, timeout, securefds));
}
else {
clients.push_back(new client(address, port, timeout, securefds));
}
}
// }
// usleep(missing*100000);
io.unlock();
// usleep(1000);
}
});
}
void queue::enqueue () {
vector<thread> worker;
lock_guard<mutex> master(io);
for (uint i=0; i<limit-clients.size(); i++) {
cout << "idemo dodati klijente " << endl;
worker.push_back(thread([&](){
if (address.empty() && srv != NULL) {
clients.push_back(new client(srv, timeout, securefds));
}
else {
cout << "dodajem " << i << endl;
clients.push_back(new client(address, port, timeout, securefds));
}
}));
}
for (uint i=0; i<worker.size(); i++) {
cout << "čekam da se dodaju " << endl;
worker[i].join();
}
cout << "dodali su se " << endl;
worker.clear();
}
client queue::dequeue() {
cout << "uzimam jednog iz stacka " << endl;
lock_guard<mutex> master(io);
client *cli;
while (true) {
if (clients.size() >= 1) {
cli = clients.front();
clients.pop_front();
cout << "uzimam jednog iz stacka uzeo sam " << endl;
return *cli;
}
else if (clients.size() == 1) {
controller = thread([&](){
cout << "Upao u tred "<< endl;
while(true) {
// io.lock();
// uint missing = limit-clients.size();
// cout << "nedostaje " << missing << endl;
// if (missing > 0.6*limit) {
// enqueue();
if (clients.size() <= 1) {
if (address.empty() && srv != NULL) {
clients.push_back(new client(srv, timeout, securefds));
}
else {
clients.push_back(new client(address, port, timeout, securefds));
}
}
// }
// usleep(missing*100000);
// io.unlock();
// usleep(1000);
sleep(1);
}
});
}
usleep(1000);
}
}
queue::~queue () {
for (uint i=0; i<clients.size(); i++) {
clients[i]->~client();
}
clients.clear();
}