From 9e14e361e37221d054fe335caf7d48023c9abab2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marcel=20Bandi=C4=87?= Date: Thu, 13 Jul 2023 19:50:17 +0200 Subject: [PATCH] Integration incoming and outgoing clients into one class --- lib/tcp_socket.hpp | 46 ++++++++-------------- src/tcp_socket.cpp | 96 ++++++++++----------------------------------- test/client.cpp | 4 +- test/client.o | Bin 40560 -> 40328 bytes test/server.cpp | 6 +-- test/server.o | Bin 40504 -> 40280 bytes 6 files changed, 42 insertions(+), 110 deletions(-) diff --git a/lib/tcp_socket.hpp b/lib/tcp_socket.hpp index d19a52f..be202c9 100644 --- a/lib/tcp_socket.hpp +++ b/lib/tcp_socket.hpp @@ -3,6 +3,7 @@ #include #include +#include #include #include #include @@ -27,6 +28,10 @@ class server { server (const ushort port, const uint limit = 1000); ~server (); + // dok god živi server žive i klijenti - klijetni moraju biti dio servera + // omogućiti enkripciju i na nivou servera + + }; /** @@ -51,40 +56,21 @@ class secure { class client { public: - int sock; + // zajedničke + int conn; // mijenja sock struct sockaddr_in addr; SSL* ssl = NULL; - - client (const string address, const ushort port, const uint timeout = 100, SSL_CTX* securefds = NULL); - ~client (); - bool tell (const string msg); - string obey (size_t byte_limit = 1024); -}; - - -/** - * Klasa za inicijalizaciju dolaznih veza - * Definira se na serverskom tipu aplikacija i predstavlja identifikator klijenta -*/ - -class comming { - public: - const server *srv; - struct sockaddr_in addr; - int conn; + // klijent sa serverom string ipv4; string ipv6; - SSL* ssl = NULL; - - comming(const server *_srv, const uint timeout = 100, SSL_CTX* securefds = NULL); - ~comming(); - bool tell (const string msg); - string obey (size_t byte_limit = 1024); - - + + // konstruktor za klijente bez servera + client (const string address, const ushort port, const uint timeout = 100, SSL_CTX* securefds = NULL); + // konstruktor za klijente sa serverom + client (const server *_srv, const uint timeout = 100, SSL_CTX* securefds = NULL); + ~client (); + bool push (const string msg); + string pull (size_t byte_limit = 1024); }; - - - #endif \ No newline at end of file diff --git a/src/tcp_socket.cpp b/src/tcp_socket.cpp index aa27589..49fe41e 100644 --- a/src/tcp_socket.cpp +++ b/src/tcp_socket.cpp @@ -110,8 +110,8 @@ secure::~secure () { client::client(const string address, const ushort port, const uint timeout, SSL_CTX* securefds) { - sock = socket(AF_INET, SOCK_STREAM, 0); - if (sock < 0) { + conn = socket(AF_INET, SOCK_STREAM, 0); + if (conn < 0) { throw string("[ERROR] Unable to open TCP socket "); } @@ -121,7 +121,7 @@ client::client(const string address, const ushort port, const uint timeout, SSL_ addr.sin_addr.s_addr = inet_addr(_address.c_str()); addr.sin_port = htons(port); - if (connect(sock, (struct sockaddr*)&addr, sizeof(struct sockaddr_in)) != 0) { + if (connect(conn, (struct sockaddr*)&addr, sizeof(struct sockaddr_in)) != 0) { throw string("Unable to connect to server "); } @@ -129,7 +129,7 @@ client::client(const string address, const ushort port, const uint timeout, SSL_ tv.tv_sec = timeout/1000; tv.tv_usec = (timeout%1000)*1000; - if (setsockopt(sock, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(struct timeval))) { + if (setsockopt(conn, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(struct timeval))) { throw string("[ERROR] Unable to set timeout "); } @@ -138,7 +138,7 @@ client::client(const string address, const ushort port, const uint timeout, SSL_ if (!ssl) { throw string("[ERROR] Creating SSL object "); } - SSL_set_fd(ssl, sock); + SSL_set_fd(ssl, conn); // Perform the SSL handshake if (SSL_connect(ssl) <= 0) { @@ -150,65 +150,6 @@ client::client(const string address, const ushort port, const uint timeout, SSL_ } - -/** - * Destruktor varijable tipa client -*/ - -client::~client () { - - if (ssl) { - SSL_shutdown(ssl); - SSL_free(ssl); - } - - if (sock <= 0) { - throw string("[ERROR] The socket is already closed "); - } - - else if (close(sock) != 0) { - throw string("[ERROR] Unable to close socket "); - } - -} - -/** - * 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::tell (const string msg) { - size_t sended = 0; - if (ssl) { - sended = SSL_write(ssl, msg.c_str(), msg.length()); - } - else { - sended = write(sock, msg.c_str(), msg.length()); - } - 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::obey (size_t byte_limit) { - char res[byte_limit] = {0}; - - if (ssl) { - SSL_read(ssl, res, byte_limit); - } - else { - read(sock , res, byte_limit); - } - - return string(res); -} - /** * Konstruktor varijable tipa commint * Prima pokazivač na inicijaliziranu varijablu tipa, port, @@ -217,8 +158,8 @@ string client::obey (size_t byte_limit) { */ -comming::comming(const server *_srv, const uint timeout, SSL_CTX* securefds) { - srv = _srv; +client::client(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) { @@ -259,11 +200,12 @@ comming::comming(const server *_srv, const uint timeout, SSL_CTX* securefds) { } + /** - * Destruktor varijable tipa comming + * Destruktor varijable tipa client */ -comming::~comming() { +client::~client () { if (ssl) { SSL_shutdown(ssl); @@ -277,16 +219,18 @@ comming::~comming() { else if (close(conn) != 0) { throw string("[ERROR] Unable to close socket "); } + } /** - * Metoda klase comming za slanje podataka preko soketa + * 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 comming::tell (const string msg) { - ssize_t sended = 0; + +bool client::push (const string msg) { + size_t sended = 0; if (ssl) { sended = SSL_write(ssl, msg.c_str(), msg.length()); } @@ -296,15 +240,14 @@ bool comming::tell (const string msg) { return sended == msg.length(); } - /** - * Metoda klase comming za primanje poruke preko soketa + * Metoda klase client za primanje poruke preko soketa * Prima dozvoljeni broj karaktera koji će primiti * Vraća string primljene poruke */ -string comming::obey (size_t byte_limit) { - char res[byte_limit] = {0}; +string client::pull (size_t byte_limit) { + char res[byte_limit] = {0}; if (ssl) { SSL_read(ssl, res, byte_limit); @@ -315,3 +258,6 @@ string comming::obey (size_t byte_limit) { return string(res); } + + + diff --git a/test/client.cpp b/test/client.cpp index 0a46f6b..8a123b1 100644 --- a/test/client.cpp +++ b/test/client.cpp @@ -17,10 +17,10 @@ int main() { string sends = "Hello world!"; - cout << myserver.tell(sends) << " " << sends.length() << endl; + cout << myserver.push(sends) << " " << sends.length() << endl; cout << "wait client " << endl; - cout << myserver.obey(); + cout << myserver.pull(); } catch (const string err) { diff --git a/test/client.o b/test/client.o index c6d1ffecaac36e626f3a487f2bbe4c636dbdcb8e..54fa244911e7925e83379a1ecd4fb5d38edb08f5 100755 GIT binary patch delta 8965 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 L*p=mNzUTh|oFfRY delta 9948 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