110 lines
2.9 KiB
C++
110 lines
2.9 KiB
C++
|
#include "sockets.h"
|
||
|
#include <array>
|
||
|
#include <memory>
|
||
|
#include <mutex>
|
||
|
|
||
|
|
||
|
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<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
|
||
|
{
|
||
|
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
|