#include "sockets.h" #include #include #include constexpr auto max_buffer = 1024; decltype(net::message_queue) net::message_queue{}; auto net::recv(const SOCKET socket) noexcept -> std::string { static std::recursive_mutex mutex{}; //allows for multiple thread access to our recv. std::unique_lock lock(mutex); try { auto& my_queue = message_queue[socket]; if (!my_queue.empty()) { std::string message = my_queue.front(); // ReSharper disable All Just shut up resharper. I would use auto here normally, but the linter wants to make message a reference.....even though it won't be valid next line. my_queue.pop(); lock.unlock(); return message; } lock.unlock(); //release lock so as to not deadlock on recv. std::string data; auto length = max_buffer; while (length == max_buffer) /* queue all future messages from the stream */ { char message_buffer[max_buffer] = { 0 }; length = ::recv(socket, message_buffer, max_buffer, 0); if (length < 0) throw std::runtime_error("Closed Socket"); for (auto i = 0; i < length; ++i) if (message_buffer[i]) /*append until null. [free message framing ;)]*/ { data += message_buffer[i]; } else { lock.lock(); my_queue.push(data); data.clear(); lock.unlock(); } } if(!data.empty()) //improperly terminated message, don't drop it! { lock.lock(); my_queue.push(data); data.clear(); lock.unlock(); } if (my_queue.empty()) /* empty response */ { return ""; } return recv(socket); //recurse. }catch(std::exception& err) /* shouldn't happen. */ { return {}; //empty } } auto net::system(const std::string& command) noexcept -> std::string { try { std::array output{ 0 }; //size is arbitrary std::string data{}; const popen_up pipe(popen(command.c_str(), "r")); if (!pipe) return ""; while (fgets(output.data(), output.size(), pipe.get()) != nullptr) data += output.data(); return data; }catch(std::exception&) { return ""; } } #if WIN32 auto net::closesocket(const SOCKET socket) noexcept -> unsigned { message_queue.erase(socket); return ::closesocket(socket); } auto net::prologue() noexcept -> void { WSADATA wsa_data; const auto result = WSAStartup(win_sock_ver, &wsa_data); if (result) { std::cout << "FATAL ERROR IN PROLOGUE " << result << std::endl; exit(-1); } } auto net::epilogue() noexcept -> void { WSACleanup(); } #else auto net::closesocket(const SOCKET socket) noexcept -> unsigned { message_queue.erase(socket); return close(socket); } auto net::prologue() noexcept -> void {} //not required on Linux. auto net::epilogue() noexcept -> void {} //not required on Linux. #endif