ClientServerProject/modernize/sockets.cpp

115 lines
3.0 KiB
C++
Executable File

#include "sockets.h"
#include <array>
#include <memory>
#include <mutex>
constexpr auto max_buffer = 1024;
decltype(net::message_queue) net::message_queue{};
auto inline mutex() -> std::recursive_mutex&
{
static std::recursive_mutex mutex{}; //allows for multiple thread access to our recv.
return mutex;
}
auto net::recv(const SOCKET socket) noexcept -> std::string
{
std::unique_lock<std::recursive_mutex> 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<char, max_buffer> 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
{
std::unique_lock<std::recursive_mutex> lock(mutex());
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