From 36ea06260fc9acc9f2cc7ed6d30be0a9dbc66d5a Mon Sep 17 00:00:00 2001 From: Substitute Date: Fri, 12 Feb 2021 03:19:18 -0500 Subject: [PATCH] Update Code --- libraries/user_input.h | 2 + main.cpp | 116 +++++++++++++++++++++++------------------ project/client.h | 2 +- project/metrics.h | 51 ++++++++++++++++++ 4 files changed, 119 insertions(+), 52 deletions(-) create mode 100644 project/metrics.h diff --git a/libraries/user_input.h b/libraries/user_input.h index 25ebc7e..4632fdb 100755 --- a/libraries/user_input.h +++ b/libraries/user_input.h @@ -30,6 +30,8 @@ namespace math } } +struct ip_validator { bool operator()(const std::string& addr) const { return net::getaddrinfo(addr, "0") != nullptr; }}; + template [[maybe_unused]] T get_input(const std::string& prompt, const std::string& on_error, std::function validator) noexcept { diff --git a/main.cpp b/main.cpp index 805b554..8c98d6f 100644 --- a/main.cpp +++ b/main.cpp @@ -1,3 +1,5 @@ +#pragma clang diagnostic push +#pragma ide diagnostic ignored "EndlessLoop" #include "modernize/sockets.h" #include "libraries/user_input.h" #include "libraries/osyncstream.h" @@ -5,6 +7,7 @@ #include "libraries/json.hpp" #include "project/server.h" #include "project/client.h" +#include "project/metrics.h" #include #include #include @@ -53,52 +56,63 @@ auto get_ip_port(int mode) if (mode == 1) ipaddress = "0.0.0.0"; else - ipaddress = get_input("Enter IP: "); + ipaddress = get_input("Enter IP: ", "Invalid IP", ip_validator()); port = get_input("Enter Port:"); return std::make_tuple(mode == 1 ? "Server" : mode == 2 ? "Client" : "Unknown", ipaddress, port); } auto execute_as_client(std::string ipaddress, std::uint16_t port) { - auto num_clients = get_input("Enter number of clients. (1-25): ", "Invalid Amount", math::is_between<0, 26>); - auto operation = get_input("Pick Command\n\t1. Date\n\t2. \n\t3. \n\t4. Netstat\n:>", "Invalid Selection", math::is_between<0, 5>); - std::vector threads{}; - threads.reserve(num_clients); - - auto internal_tracker = 0; //Stats tracking. - std::vector internal_stats{}; - internal_stats.reserve(num_clients); - for(int i = 0; i < num_clients; ++i) + while(true) { - threads.emplace_back(std::thread([&]() - { - client net_client{}; - auto start = std::chrono::high_resolution_clock::now(); - net_client.message_received += [start, &internal_tracker, &internal_stats](std::string& message) - { - auto end = std::chrono::high_resolution_clock::now(); - std::chrono::duration elapsed = end-start; - sscout << internal_tracker << math::numerical_suffix(internal_tracker++) << " client response\n" - << message - << "Round Trip Was " << elapsed.count() << " ms\n" - << hr; - internal_stats.emplace_back(elapsed.count()); //Stats tracking - return true; - }; - auto res = net_client.connect(ipaddress, port); - if (res.joinable()) - { - net_client.send_message(std::to_string(operation)); - res.join(); - } - })); - } - sscout << "All clients connected.\n" << hr; - for(auto&& thread : threads) - thread.join(); - sscout << "Finished!\n"; - json metrics_json = internal_stats; + auto operation = get_input("Pick Command\n\t1. Date\n\t2. \n\t3. \n\t4. Netstat\n\t5.\n\t6.\n\t7. Exit\n:>", "Invalid Selection", math::is_between<0, 8>); + if (operation == 7) + break; + auto num_clients = get_input("Enter number of clients. (1-25): ", "Invalid Amount", math::is_between<0, 26>); + std::vector threads{}; + threads.reserve(num_clients); + metrics::job this_job{}; + this_job.operation = operation; + this_job.client_metrics.reserve(num_clients); + for(int i = 0; i < num_clients; ++i) + { + threads.emplace_back(std::thread([&]() + { + client net_client{}; + metrics::time_tracker clock{}; + //Callback + net_client.message_received += [&](std::string& message) + { + auto elapsed = clock.elapsed(); + auto client_num = this_job.client_metrics.size()+1; + //UX + sscout << client_num << math::numerical_suffix(client_num) << " client response\n" + << message + << "Round Trip Was " << elapsed.count() << " ms\n" + << hr; + //Metrics + metrics::client_message client_metric{}; + client_metric.client_number = client_num; + client_metric.trip_time = elapsed.count(); + this_job.client_metrics.emplace_back(client_metric); //this isn't thread safe! + return true; + }; + auto res = net_client.connect(ipaddress, port); + if (res.joinable()) + { + net_client.send_message(std::to_string(operation)); + res.join(); + } + })); + } + sscout << "All clients connected.\n" << hr; + for(auto&& thread : threads) + thread.join(); + metrics::job_metrics.emplace_back(this_job); + sscout << "Finished!\n"; + } + json metrics_json{metrics::job_metrics}; std::ofstream metrics; metrics.open ("metrics.json", std::ios::out | std::ios::trunc); //again, metrics data metrics << metrics_json; @@ -110,17 +124,17 @@ int main() { net::prologue(); const auto mode = get_input("Select Mode\n\t1. Server\n\t2. Client\n>:", "Invalid Choice!", math::is_between<0, 3>); - while (true) { - auto[prefix, ipaddress, port] = get_ip_port(mode); - sscout << prefix << " selected, ip/port is " << ipaddress << "/" << port << ".\n"; - if (mode == 2) { - execute_as_client(ipaddress, port); - } else { - server net_server{}; - - net_server.client_connected += onClientConnected; - net_server.message_received += onClientMessaged; - net_server.listen(ipaddress, port).join(); - } + auto[prefix, ipaddress, port] = get_ip_port(mode); + sscout << prefix << " selected, ip/port is " << ipaddress << "/" << port << ".\n"; + if (mode == 2) { + execute_as_client(ipaddress, port); + } else { + server net_server{}; + net_server.client_connected += onClientConnected; + net_server.message_received += onClientMessaged; + net_server.listen(ipaddress, port).join(); } -} \ No newline at end of file + net::epilogue(); + return 0; +} +#pragma clang diagnostic pop \ No newline at end of file diff --git a/project/client.h b/project/client.h index b3afa83..b75a3e1 100644 --- a/project/client.h +++ b/project/client.h @@ -25,7 +25,7 @@ public: { const auto address = net::getaddrinfo(ip, std::to_string(port)); auto remote_address = net::getaddrinfo(ip, std::to_string(port)); - if (net::connect(socket, remote_address) < 0) + if (!remote_address || net::connect(socket, remote_address) < 0) return std::thread(); auto thread = std::thread(&client::accept_message, this); return thread; diff --git a/project/metrics.h b/project/metrics.h new file mode 100644 index 0000000..87f687d --- /dev/null +++ b/project/metrics.h @@ -0,0 +1,51 @@ +#pragma once +//#include "libraries/json.hpp" +using json = nlohmann::json; + +namespace metrics +{ + class time_tracker + { + std::chrono::high_resolution_clock::time_point start = std::chrono::high_resolution_clock::now(); + public: + template + std::chrono::duration elapsed() const + { + return std::chrono::duration(std::chrono::high_resolution_clock::now() - start); + } + }; + struct client_message + { + int client_number; + double trip_time; + }; + + inline void to_json(json& j, const client_message& p) + { + j = json{ + { "client_number", p.client_number }, + { "trip_time", p.trip_time } + }; + } + + struct job + { + int operation; + std::vector client_metrics{}; + }; + + inline void to_json(json& j, const job& p) + { + auto total_trip_time = 0.; + for(auto&& client : p.client_metrics) + total_trip_time += client.trip_time; + j = json{ + { "operation", p.operation }, + { "total_trip_time", total_trip_time }, + { "average_trip_time",total_trip_time / p.client_metrics.size() }, + { "client_metrics", p.client_metrics} + }; + } + + inline std::vector job_metrics{}; +} \ No newline at end of file