Initial push
This commit is contained in:
parent
fd168765cb
commit
837b134087
6
CMakeLists.txt
Normal file
6
CMakeLists.txt
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
cmake_minimum_required(VERSION 3.17)
|
||||||
|
project(Project_1)
|
||||||
|
|
||||||
|
set(CMAKE_CXX_STANDARD 17)
|
||||||
|
|
||||||
|
add_executable(Project_1 main.cpp sockets.cpp switch_expression.h)
|
83
client.cpp
Executable file
83
client.cpp
Executable file
@ -0,0 +1,83 @@
|
|||||||
|
#include <stdio.h>
|
||||||
|
#include <sys/socket.h>
|
||||||
|
#include <arpa/inet.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <string>
|
||||||
|
#include <queue>
|
||||||
|
#define max_buffer 1024
|
||||||
|
#define PORT 666
|
||||||
|
#define SOCKET int
|
||||||
|
|
||||||
|
std::queue<std::string> message_queue_{};
|
||||||
|
auto ufnk(const SOCKET socket)->std::string
|
||||||
|
{
|
||||||
|
if (!message_queue_.empty())
|
||||||
|
{
|
||||||
|
auto message = message_queue_.front();
|
||||||
|
message_queue_.pop();
|
||||||
|
return message;
|
||||||
|
}
|
||||||
|
std::string data;
|
||||||
|
char message_buffer[max_buffer] = { 0 };
|
||||||
|
auto length = max_buffer;
|
||||||
|
while (length == max_buffer)
|
||||||
|
{
|
||||||
|
length = ::recv(socket, message_buffer, max_buffer, 0);
|
||||||
|
for (auto i = 0; i < length; ++i)
|
||||||
|
if(message_buffer[i]) /*append until null.*/
|
||||||
|
{
|
||||||
|
data += message_buffer[i];
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
message_queue_.push(data);
|
||||||
|
data.clear();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (message_queue_.empty())
|
||||||
|
return "";
|
||||||
|
return ufnk(socket); //recurse.
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
int main(int argc, char const *argv[])
|
||||||
|
{
|
||||||
|
int sock = 0, valread = 1;
|
||||||
|
struct sockaddr_in serv_addr;
|
||||||
|
char *hello = "4";
|
||||||
|
char buffer[1024] = {0};
|
||||||
|
if ((sock = socket(AF_INET, SOCK_STREAM, 0)) < 0)
|
||||||
|
{
|
||||||
|
printf("\n Socket creation error \n");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
serv_addr.sin_family = AF_INET;
|
||||||
|
serv_addr.sin_port = htons(PORT);
|
||||||
|
|
||||||
|
// Convert IPv4 and IPv6 addresses from text to binary form
|
||||||
|
if(inet_pton(AF_INET, "127.0.0.1", &serv_addr.sin_addr)<=0)
|
||||||
|
{
|
||||||
|
printf("\nInvalid address/ Address not supported \n");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (connect(sock, (struct sockaddr *)&serv_addr, sizeof(serv_addr)) < 0)
|
||||||
|
{
|
||||||
|
printf("\nConnection Failed \n");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
send(sock , hello , strlen(hello)+1 , 0 );
|
||||||
|
printf("sent date command\n");
|
||||||
|
|
||||||
|
|
||||||
|
while(true)
|
||||||
|
{
|
||||||
|
auto text = ufnk(sock);
|
||||||
|
if (text == "")
|
||||||
|
break;
|
||||||
|
printf("%s\n", text.c_str());
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
BIN
cmake-build-debug/Project_1
Executable file
BIN
cmake-build-debug/Project_1
Executable file
Binary file not shown.
97
cmake-build-debug/Project_1.cbp
Normal file
97
cmake-build-debug/Project_1.cbp
Normal file
@ -0,0 +1,97 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<CodeBlocks_project_file>
|
||||||
|
<FileVersion major="1" minor="6"/>
|
||||||
|
<Project>
|
||||||
|
<Option title="Project_1"/>
|
||||||
|
<Option makefile_is_custom="1"/>
|
||||||
|
<Option compiler="gcc"/>
|
||||||
|
<Option virtualFolders="CMake Files\;"/>
|
||||||
|
<Build>
|
||||||
|
<Target title="all">
|
||||||
|
<Option working_dir="/Users/riley/CLionProjects/UNF/Networks/Project 1/cmake-build-debug"/>
|
||||||
|
<Option type="4"/>
|
||||||
|
<MakeCommands>
|
||||||
|
<Build command="/usr/bin/make -j8 -f "/Users/riley/CLionProjects/UNF/Networks/Project\ 1/cmake-build-debug/Makefile" VERBOSE=1 all"/>
|
||||||
|
<CompileFile command="/usr/bin/make -j8 -f "/Users/riley/CLionProjects/UNF/Networks/Project\ 1/cmake-build-debug/Makefile" VERBOSE=1 "$file""/>
|
||||||
|
<Clean command="/usr/bin/make -j8 -f "/Users/riley/CLionProjects/UNF/Networks/Project\ 1/cmake-build-debug/Makefile" VERBOSE=1 clean"/>
|
||||||
|
<DistClean command="/usr/bin/make -j8 -f "/Users/riley/CLionProjects/UNF/Networks/Project\ 1/cmake-build-debug/Makefile" VERBOSE=1 clean"/>
|
||||||
|
</MakeCommands>
|
||||||
|
</Target>
|
||||||
|
<Target title="rebuild_cache">
|
||||||
|
<Option working_dir="/Users/riley/CLionProjects/UNF/Networks/Project 1/cmake-build-debug"/>
|
||||||
|
<Option type="4"/>
|
||||||
|
<MakeCommands>
|
||||||
|
<Build command="/usr/bin/make -j8 -f "/Users/riley/CLionProjects/UNF/Networks/Project\ 1/cmake-build-debug/Makefile" VERBOSE=1 rebuild_cache"/>
|
||||||
|
<CompileFile command="/usr/bin/make -j8 -f "/Users/riley/CLionProjects/UNF/Networks/Project\ 1/cmake-build-debug/Makefile" VERBOSE=1 "$file""/>
|
||||||
|
<Clean command="/usr/bin/make -j8 -f "/Users/riley/CLionProjects/UNF/Networks/Project\ 1/cmake-build-debug/Makefile" VERBOSE=1 clean"/>
|
||||||
|
<DistClean command="/usr/bin/make -j8 -f "/Users/riley/CLionProjects/UNF/Networks/Project\ 1/cmake-build-debug/Makefile" VERBOSE=1 clean"/>
|
||||||
|
</MakeCommands>
|
||||||
|
</Target>
|
||||||
|
<Target title="edit_cache">
|
||||||
|
<Option working_dir="/Users/riley/CLionProjects/UNF/Networks/Project 1/cmake-build-debug"/>
|
||||||
|
<Option type="4"/>
|
||||||
|
<MakeCommands>
|
||||||
|
<Build command="/usr/bin/make -j8 -f "/Users/riley/CLionProjects/UNF/Networks/Project\ 1/cmake-build-debug/Makefile" VERBOSE=1 edit_cache"/>
|
||||||
|
<CompileFile command="/usr/bin/make -j8 -f "/Users/riley/CLionProjects/UNF/Networks/Project\ 1/cmake-build-debug/Makefile" VERBOSE=1 "$file""/>
|
||||||
|
<Clean command="/usr/bin/make -j8 -f "/Users/riley/CLionProjects/UNF/Networks/Project\ 1/cmake-build-debug/Makefile" VERBOSE=1 clean"/>
|
||||||
|
<DistClean command="/usr/bin/make -j8 -f "/Users/riley/CLionProjects/UNF/Networks/Project\ 1/cmake-build-debug/Makefile" VERBOSE=1 clean"/>
|
||||||
|
</MakeCommands>
|
||||||
|
</Target>
|
||||||
|
<Target title="Project_1">
|
||||||
|
<Option output="/Users/riley/CLionProjects/UNF/Networks/Project 1/cmake-build-debug/Project_1" prefix_auto="0" extension_auto="0"/>
|
||||||
|
<Option working_dir="/Users/riley/CLionProjects/UNF/Networks/Project 1/cmake-build-debug"/>
|
||||||
|
<Option object_output="./"/>
|
||||||
|
<Option type="1"/>
|
||||||
|
<Option compiler="gcc"/>
|
||||||
|
<Compiler>
|
||||||
|
<Add directory="/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/../include/c++/v1"/>
|
||||||
|
<Add directory="/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/lib/clang/12.0.0/include"/>
|
||||||
|
<Add directory="/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include"/>
|
||||||
|
<Add directory="/System/Library/Frameworks"/>
|
||||||
|
<Add directory="/Library/Frameworks"/>
|
||||||
|
</Compiler>
|
||||||
|
<MakeCommands>
|
||||||
|
<Build command="/usr/bin/make -j8 -f "/Users/riley/CLionProjects/UNF/Networks/Project\ 1/cmake-build-debug/Makefile" VERBOSE=1 Project_1"/>
|
||||||
|
<CompileFile command="/usr/bin/make -j8 -f "/Users/riley/CLionProjects/UNF/Networks/Project\ 1/cmake-build-debug/Makefile" VERBOSE=1 "$file""/>
|
||||||
|
<Clean command="/usr/bin/make -j8 -f "/Users/riley/CLionProjects/UNF/Networks/Project\ 1/cmake-build-debug/Makefile" VERBOSE=1 clean"/>
|
||||||
|
<DistClean command="/usr/bin/make -j8 -f "/Users/riley/CLionProjects/UNF/Networks/Project\ 1/cmake-build-debug/Makefile" VERBOSE=1 clean"/>
|
||||||
|
</MakeCommands>
|
||||||
|
</Target>
|
||||||
|
<Target title="Project_1/fast">
|
||||||
|
<Option output="/Users/riley/CLionProjects/UNF/Networks/Project 1/cmake-build-debug/Project_1" prefix_auto="0" extension_auto="0"/>
|
||||||
|
<Option working_dir="/Users/riley/CLionProjects/UNF/Networks/Project 1/cmake-build-debug"/>
|
||||||
|
<Option object_output="./"/>
|
||||||
|
<Option type="1"/>
|
||||||
|
<Option compiler="gcc"/>
|
||||||
|
<Compiler>
|
||||||
|
<Add directory="/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/../include/c++/v1"/>
|
||||||
|
<Add directory="/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/lib/clang/12.0.0/include"/>
|
||||||
|
<Add directory="/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include"/>
|
||||||
|
<Add directory="/System/Library/Frameworks"/>
|
||||||
|
<Add directory="/Library/Frameworks"/>
|
||||||
|
</Compiler>
|
||||||
|
<MakeCommands>
|
||||||
|
<Build command="/usr/bin/make -j8 -f "/Users/riley/CLionProjects/UNF/Networks/Project\ 1/cmake-build-debug/Makefile" VERBOSE=1 Project_1/fast"/>
|
||||||
|
<CompileFile command="/usr/bin/make -j8 -f "/Users/riley/CLionProjects/UNF/Networks/Project\ 1/cmake-build-debug/Makefile" VERBOSE=1 "$file""/>
|
||||||
|
<Clean command="/usr/bin/make -j8 -f "/Users/riley/CLionProjects/UNF/Networks/Project\ 1/cmake-build-debug/Makefile" VERBOSE=1 clean"/>
|
||||||
|
<DistClean command="/usr/bin/make -j8 -f "/Users/riley/CLionProjects/UNF/Networks/Project\ 1/cmake-build-debug/Makefile" VERBOSE=1 clean"/>
|
||||||
|
</MakeCommands>
|
||||||
|
</Target>
|
||||||
|
</Build>
|
||||||
|
<Unit filename="/Users/riley/CLionProjects/UNF/Networks/Project 1/main.cpp">
|
||||||
|
<Option target="Project_1"/>
|
||||||
|
</Unit>
|
||||||
|
<Unit filename="/Users/riley/CLionProjects/UNF/Networks/Project 1/sockets.cpp">
|
||||||
|
<Option target="Project_1"/>
|
||||||
|
</Unit>
|
||||||
|
<Unit filename="/Users/riley/CLionProjects/UNF/Networks/Project 1/sockets.h">
|
||||||
|
<Option target="Project_1"/>
|
||||||
|
</Unit>
|
||||||
|
<Unit filename="/Users/riley/CLionProjects/UNF/Networks/Project 1/switch_expression.h">
|
||||||
|
<Option target="Project_1"/>
|
||||||
|
</Unit>
|
||||||
|
<Unit filename="/Users/riley/CLionProjects/UNF/Networks/Project 1/CMakeLists.txt">
|
||||||
|
<Option virtualFolder="CMake Files\"/>
|
||||||
|
</Unit>
|
||||||
|
</Project>
|
||||||
|
</CodeBlocks_project_file>
|
24
event.h
Executable file
24
event.h
Executable file
@ -0,0 +1,24 @@
|
|||||||
|
#pragma once
|
||||||
|
#include <functional>
|
||||||
|
|
||||||
|
template<typename ...T>
|
||||||
|
class event
|
||||||
|
{
|
||||||
|
std::vector <std::function<bool(T...)>> callbacks;
|
||||||
|
public:
|
||||||
|
auto operator+=(std::function<bool(T...)> callback) -> event<T...>&
|
||||||
|
{
|
||||||
|
callbacks.emplace_back(callback);
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
auto reset() -> void
|
||||||
|
{
|
||||||
|
callbacks.clear();
|
||||||
|
}
|
||||||
|
auto execute(T... data) -> void
|
||||||
|
{
|
||||||
|
for (auto& callback : callbacks)
|
||||||
|
if (callback(data...)) /* true = handled... */
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
};
|
8
linux.h
Executable file
8
linux.h
Executable file
@ -0,0 +1,8 @@
|
|||||||
|
#pragma once
|
||||||
|
#include <netdb.h>
|
||||||
|
#include <netinet/in.h>
|
||||||
|
#include <sys/socket.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#define SOCKET_ERROR -1
|
||||||
|
#define INVALID_SOCKET 0
|
||||||
|
typedef int SOCKET;
|
207
main.cpp
Normal file
207
main.cpp
Normal file
@ -0,0 +1,207 @@
|
|||||||
|
#if WIN32
|
||||||
|
#define NOMINMAX
|
||||||
|
#endif
|
||||||
|
#include "sockets.h"
|
||||||
|
#include "user_input.h"
|
||||||
|
#include <chrono>
|
||||||
|
#include "server.h"
|
||||||
|
#include "osyncstream.h"
|
||||||
|
#include "switch_expression.h"
|
||||||
|
|
||||||
|
/*
|
||||||
|
*
|
||||||
|
auto server(const addrinfo_up& address) noexcept -> void
|
||||||
|
{
|
||||||
|
|
||||||
|
const auto socket = net::socket(AF_INET, SOCK_STREAM, 0);
|
||||||
|
net::bind(socket, address);
|
||||||
|
net::listen(socket);
|
||||||
|
std::cout << "[ Server Started ]" << std::endl;
|
||||||
|
|
||||||
|
//below needs update.
|
||||||
|
do
|
||||||
|
{
|
||||||
|
auto const client = accept(socket, nullptr, nullptr);
|
||||||
|
if (!client)
|
||||||
|
continue;
|
||||||
|
printf("Got a client.\n");
|
||||||
|
|
||||||
|
|
||||||
|
auto operation = net::recv(client);
|
||||||
|
try
|
||||||
|
{
|
||||||
|
switch (std::stoi(operation))
|
||||||
|
{
|
||||||
|
case 1:
|
||||||
|
net::send(client, net::system("date /t"));
|
||||||
|
break;
|
||||||
|
case 4:
|
||||||
|
net::send(client, net::system("netstat -ant"));
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
net::send(client, "INVALID INPUT!");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (std::exception&)
|
||||||
|
{
|
||||||
|
net::send(client, "INVALID INPUT!");
|
||||||
|
}
|
||||||
|
net::send(client, "<=> T123 <=>");
|
||||||
|
net::closesocket(client);
|
||||||
|
printf("Closed Client\n");
|
||||||
|
|
||||||
|
} while (true);
|
||||||
|
}
|
||||||
|
|
||||||
|
auto client(const addrinfo_up& address) noexcept -> void
|
||||||
|
{
|
||||||
|
while(true)
|
||||||
|
{
|
||||||
|
const auto operation = get_input<unsigned int>("Select Command:\n\t1. Date", "");
|
||||||
|
const auto total_clients = get_input<unsigned int>("Input Number of Clients: ", "");
|
||||||
|
|
||||||
|
std::vector<std::thread> threads;
|
||||||
|
|
||||||
|
for(auto i = 0u; i < total_clients; ++i)
|
||||||
|
{
|
||||||
|
const auto prefix = "[client " + std::to_string(i) + "]: ";
|
||||||
|
try
|
||||||
|
{
|
||||||
|
threads.emplace_back(std::thread([=, &address]()
|
||||||
|
{
|
||||||
|
sscout << prefix << "started" << std::endl;
|
||||||
|
const auto socket = net::socket(AF_INET, SOCK_STREAM, 0);
|
||||||
|
net::connect(socket, address);
|
||||||
|
net::send(socket, std::to_string(operation));
|
||||||
|
sscout << prefix << "sent command" << std::endl;
|
||||||
|
const auto& response = net::recv(socket);
|
||||||
|
sscout << prefix << "got response\n" << prefix << response << std::endl;
|
||||||
|
net::closesocket(socket);
|
||||||
|
sscout << prefix << "closed" << std::endl;
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
catch (std::exception&) { sscout << prefix << " failed to init " << std::endl; }
|
||||||
|
}
|
||||||
|
for (auto& thread : threads)
|
||||||
|
thread.join();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
bool onClientConnected(networked& client)
|
||||||
|
{
|
||||||
|
sscout << "Accepted Client on Server.\n";
|
||||||
|
|
||||||
|
using namespace std::chrono_literals;
|
||||||
|
auto start = std::chrono::high_resolution_clock::now();
|
||||||
|
//std::this_thread::sleep_for(2000ms);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
auto onClientMessaged(networked& client, std::string& message)
|
||||||
|
{
|
||||||
|
sscout << "message was " << message << "\n";
|
||||||
|
|
||||||
|
client.send_message("thanks for the message!");
|
||||||
|
client.disconnect();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline auto resolve_selection(const int selection) -> std::string
|
||||||
|
{
|
||||||
|
if (selection == 1)
|
||||||
|
return "Server";
|
||||||
|
else if (selection == 2)
|
||||||
|
return "Client";
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
|
int main()
|
||||||
|
{
|
||||||
|
net::prologue();
|
||||||
|
const auto mode = get_input<int>("Select Mode\n\t1. Server\n\t2. Client\n>:","Invalid Choice!",math::is_between<0,3>);
|
||||||
|
while (true)
|
||||||
|
{
|
||||||
|
std::string ipaddress{};
|
||||||
|
std::uint16_t port{};
|
||||||
|
if (mode == 1)
|
||||||
|
ipaddress = "0.0.0.0";
|
||||||
|
else
|
||||||
|
ipaddress = get_input<std::string>("Enter IP: ");
|
||||||
|
port = get_input<std::uint16_t>("Enter Port:");
|
||||||
|
sscout << resolve_selection(mode) << " selected, ip/port is " << ipaddress << "/" << port << ".\n";
|
||||||
|
if (mode == 2)
|
||||||
|
{
|
||||||
|
client net_client{};
|
||||||
|
net_client.message_received += [](std::string& what){
|
||||||
|
sscout << "GOT " << what << ".\n";
|
||||||
|
return true;
|
||||||
|
};
|
||||||
|
if(net_client.connect(ipaddress, port))
|
||||||
|
{
|
||||||
|
net_client.send_message("!Hello!\n");
|
||||||
|
net_client.send_message("Second Message\n");
|
||||||
|
net_client.send_message("Final Message!\n");
|
||||||
|
while(net_client.is_valid()){}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
sscout << "NO NET\n";
|
||||||
|
}
|
||||||
|
net_client.disconnect();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
auto listen_server = []()
|
||||||
|
{
|
||||||
|
server srv{};
|
||||||
|
srv.client_connected += onClientConnected;
|
||||||
|
srv.message_received += onClientMessaged;
|
||||||
|
auto thread = srv.listen("0.0.0.0", 80);
|
||||||
|
if(thread.joinable())
|
||||||
|
{
|
||||||
|
thread.join();
|
||||||
|
}else{
|
||||||
|
sscout << "Thread is bad!\n";
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
net::epilogue();
|
||||||
|
return 0;
|
||||||
|
/*
|
||||||
|
*
|
||||||
|
const auto selection = get_input<int>("Select Mode\n\t1. Server\n\t2. Client\n>:", "Invalid Choice!", [](const auto x) {return x > 0 && x < 3; });
|
||||||
|
|
||||||
|
auto address = std::string("0.0.0.0");
|
||||||
|
auto port = 0_p;
|
||||||
|
addrinfo_up address_binding{};
|
||||||
|
while(!address_binding.get())
|
||||||
|
{
|
||||||
|
if (selection == 2)
|
||||||
|
address = get_input<std::string>("Enter Remote IP (aaa.bbb.ccc.ddd): ", "");
|
||||||
|
port = get_input<unsigned short>("Enter Port: ", "");
|
||||||
|
address_binding = net::getaddrinfo(address, std::to_string(port));
|
||||||
|
}
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
std::cout << "You have selected " << (selection == 1 ? "Server" : "Client") << std::endl;
|
||||||
|
std::cout << "Address: " << address << ":" << port << std::endl;
|
||||||
|
}catch(std::exception&){}
|
||||||
|
|
||||||
|
|
||||||
|
if (selection == 2)
|
||||||
|
client(address_binding);
|
||||||
|
else
|
||||||
|
listen_server(address_binding);
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
net::epilogue();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
64
osyncstream.h
Executable file
64
osyncstream.h
Executable file
@ -0,0 +1,64 @@
|
|||||||
|
/*
|
||||||
|
* osyncstream - A wrapper around ostream that properly locks while consuming stream operators
|
||||||
|
* Copyright Riley Strickland 2021
|
||||||
|
*/
|
||||||
|
#pragma once
|
||||||
|
#include <mutex>
|
||||||
|
#include <ostream>
|
||||||
|
#include <iostream>
|
||||||
|
|
||||||
|
class osyncstream_locked final
|
||||||
|
{
|
||||||
|
std::unique_lock<std::recursive_mutex> lock_;
|
||||||
|
std::ostream& stream_;
|
||||||
|
public:
|
||||||
|
osyncstream_locked(std::recursive_mutex& mtx, std::ostream& stream) : lock_(mtx), stream_(stream) {} //construct and lock the mutex from osyncstream.
|
||||||
|
osyncstream_locked(const osyncstream_locked& o) = delete;
|
||||||
|
/* A recursive mutex isn't great here (reference forwarding would be better except...only a recursive mutex can assure lock-before-release on ownership transfer */
|
||||||
|
template<typename T>
|
||||||
|
friend auto operator<<(osyncstream_locked&& os, const T& d) -> osyncstream_locked&&
|
||||||
|
{
|
||||||
|
os.stream_ << d;
|
||||||
|
return std::move(os); /* move semantics :( ... thanks Clang/GCC */
|
||||||
|
}
|
||||||
|
friend auto operator<<(osyncstream_locked&& os, std::ostream& (*func)(std::ostream&)) -> std::ostream&
|
||||||
|
{
|
||||||
|
return os.stream_ << func;
|
||||||
|
}
|
||||||
|
osyncstream_locked(osyncstream_locked&& o) noexcept : osyncstream_locked(*o.lock_.mutex(), o.stream_) {} //could be better... max depth 2, usual depth 1.
|
||||||
|
osyncstream_locked& operator=(osyncstream_locked&) = delete; //disallow
|
||||||
|
osyncstream_locked& operator=(osyncstream_locked&&) = delete; //disallow
|
||||||
|
~osyncstream_locked() = default;
|
||||||
|
};
|
||||||
|
|
||||||
|
class osyncstream final
|
||||||
|
{
|
||||||
|
std::recursive_mutex mutex_{};
|
||||||
|
std::ostream& stream_;
|
||||||
|
public:
|
||||||
|
explicit osyncstream(std::ostream& stream) : stream_(stream) {}
|
||||||
|
template<typename T>
|
||||||
|
friend auto operator<<(osyncstream& os, const T& d) -> osyncstream_locked
|
||||||
|
{
|
||||||
|
std::unique_lock<std::recursive_mutex> lock(os.mutex_);
|
||||||
|
os.stream_ << d;
|
||||||
|
return osyncstream_locked { os.mutex_, os.stream_ };
|
||||||
|
}
|
||||||
|
friend auto operator<<(osyncstream& os, std::ostream& (*func)(std::ostream&)) -> osyncstream_locked
|
||||||
|
{
|
||||||
|
std::unique_lock<std::recursive_mutex> lock(os.mutex_);
|
||||||
|
os.stream_ << func;
|
||||||
|
return osyncstream_locked{ os.mutex_, os.stream_ };
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
struct streams
|
||||||
|
{
|
||||||
|
static std::ostream& cout;
|
||||||
|
static std::ostream& cerr;
|
||||||
|
};
|
||||||
|
|
||||||
|
inline std::ostream& streams::cout = std::cout;
|
||||||
|
inline std::ostream& streams::cerr = std::cerr;
|
||||||
|
inline osyncstream sscout(streams::cout);
|
||||||
|
inline osyncstream sscerr(streams::cerr);
|
112
server.h
Executable file
112
server.h
Executable file
@ -0,0 +1,112 @@
|
|||||||
|
#pragma once
|
||||||
|
#include "sockets.h"
|
||||||
|
#include "event.h"
|
||||||
|
#include <thread>
|
||||||
|
|
||||||
|
#include "osyncstream.h"
|
||||||
|
|
||||||
|
class networked
|
||||||
|
{
|
||||||
|
protected:
|
||||||
|
SOCKET socket;
|
||||||
|
public:
|
||||||
|
explicit networked(SOCKET existing)
|
||||||
|
{
|
||||||
|
socket = existing;
|
||||||
|
}
|
||||||
|
auto disconnect() -> void
|
||||||
|
{
|
||||||
|
if (!is_valid()) return; //socket is closed.
|
||||||
|
net::closesocket(socket);
|
||||||
|
socket = INVALID_SOCKET;
|
||||||
|
}
|
||||||
|
[[nodiscard]] auto receive_message() const -> std::string { if (!is_valid()) return ""; return net::recv(socket); }
|
||||||
|
auto send_message(const std::string& message) const -> void { if (!is_valid()) return; net::send(socket, message);}
|
||||||
|
[[nodiscard]] auto is_valid() const -> bool { return socket != INVALID_SOCKET; }
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
class client : public networked
|
||||||
|
{
|
||||||
|
auto accept_message() -> void //add const later
|
||||||
|
{
|
||||||
|
while(is_valid()) /* Accept all messages */
|
||||||
|
{
|
||||||
|
auto message = receive_message();
|
||||||
|
if (message.empty() && is_valid())
|
||||||
|
{
|
||||||
|
sscout << "Server Connection Lost\n";
|
||||||
|
disconnect();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
message_received.execute(message);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
public:
|
||||||
|
event<std::string&> message_received {};
|
||||||
|
explicit client() : networked(net::socket(AF_INET, SOCK_STREAM, 0)){}
|
||||||
|
auto connect(const std::string& ip, const port port) -> bool
|
||||||
|
{
|
||||||
|
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)
|
||||||
|
return false;
|
||||||
|
std::thread(&client::accept_message, this).detach();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
class server : networked
|
||||||
|
{
|
||||||
|
auto accept_clients() -> void
|
||||||
|
{
|
||||||
|
while(is_valid())
|
||||||
|
{
|
||||||
|
auto client = networked(accept(socket, nullptr, nullptr));
|
||||||
|
client_connected.execute(client);
|
||||||
|
#ifdef THREADED
|
||||||
|
std::thread(&server::accept_client_message, this, client).detach();
|
||||||
|
#else
|
||||||
|
std::thread(&server::accept_client_message, this, client).join();
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
auto accept_client_message(networked client) -> void //add const later
|
||||||
|
{
|
||||||
|
using namespace std::chrono_literals;
|
||||||
|
std::this_thread::sleep_for(2000ms);
|
||||||
|
while(client.is_valid()) /* Accept all messages */
|
||||||
|
{
|
||||||
|
auto message = client.receive_message();
|
||||||
|
if (message.empty() && client.is_valid()) //Client could have disconnected after check but PRIOR to recv.
|
||||||
|
{
|
||||||
|
sscout << "Client Connection Lost\n";
|
||||||
|
client.disconnect();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
message_received.execute(client, message);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public:
|
||||||
|
event<networked&> client_connected {};
|
||||||
|
event<networked&, std::string&> message_received {};
|
||||||
|
auto listen(const std::string& ip, const port port) -> std::thread
|
||||||
|
{
|
||||||
|
const auto address = net::getaddrinfo(ip, std::to_string(port));
|
||||||
|
if (net::bind(socket, address) < 0)
|
||||||
|
{
|
||||||
|
sscout << "Error binding.\n";
|
||||||
|
return std::thread();
|
||||||
|
}
|
||||||
|
if (net::listen(socket) < 0)
|
||||||
|
{
|
||||||
|
sscout << "Error listening.\n";
|
||||||
|
return std::thread();
|
||||||
|
}
|
||||||
|
sscout << "Listening ... \n";
|
||||||
|
return std::thread(&server::accept_clients, this);
|
||||||
|
}
|
||||||
|
explicit server() : networked(net::socket(AF_INET, SOCK_STREAM, 0)){}
|
||||||
|
};
|
112
sockets.cpp
Executable file
112
sockets.cpp
Executable file
@ -0,0 +1,112 @@
|
|||||||
|
#include "sockets.h"
|
||||||
|
#include "osyncstream.h"
|
||||||
|
#include <array>
|
||||||
|
#include <iostream>
|
||||||
|
#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
|
63
sockets.h
Executable file
63
sockets.h
Executable file
@ -0,0 +1,63 @@
|
|||||||
|
#pragma once
|
||||||
|
#if WIN32
|
||||||
|
#include "windows.h"
|
||||||
|
#else
|
||||||
|
#include "linux.h"
|
||||||
|
#endif
|
||||||
|
#include <cstring>
|
||||||
|
#include <functional>
|
||||||
|
#include <iostream>
|
||||||
|
#include <memory>
|
||||||
|
#include <queue>
|
||||||
|
#include <string>
|
||||||
|
#include <unordered_map>
|
||||||
|
|
||||||
|
typedef std::uint16_t port;
|
||||||
|
inline port operator "" _p(const unsigned long long value) { return static_cast<port>(value); }
|
||||||
|
|
||||||
|
struct addrinfo_up_deleter { void operator()(addrinfo* ptr) const { ::freeaddrinfo(ptr); } };
|
||||||
|
typedef std::unique_ptr<addrinfo, addrinfo_up_deleter> addrinfo_up; /* Modern C++ shared pointer for addrinfo. */
|
||||||
|
|
||||||
|
struct popen_up_deleter { void operator()(FILE* ptr) const { pclose(ptr); } };
|
||||||
|
typedef std::unique_ptr<FILE, popen_up_deleter> popen_up; /* Modern C++ shared pointer for popen. */
|
||||||
|
|
||||||
|
class net
|
||||||
|
{
|
||||||
|
// ReSharper disable once CppInconsistentNaming
|
||||||
|
static std::unordered_map<SOCKET, std::queue<std::string>> message_queue;
|
||||||
|
public:
|
||||||
|
/* inlined functions*/
|
||||||
|
static auto bind(SOCKET socket, const addrinfo_up& address) noexcept -> int
|
||||||
|
{
|
||||||
|
return ::bind(socket, address->ai_addr, address->ai_addrlen);
|
||||||
|
}
|
||||||
|
static auto connect(SOCKET socket, const addrinfo_up& address) noexcept -> int
|
||||||
|
{
|
||||||
|
return ::connect(socket, address->ai_addr, address->ai_addrlen);
|
||||||
|
}
|
||||||
|
static auto getaddrinfo(const std::string& address, const std::string& port) noexcept -> addrinfo_up
|
||||||
|
{
|
||||||
|
addrinfo* result{};
|
||||||
|
addrinfo hints{};
|
||||||
|
memset(&hints, 0, sizeof(hints));
|
||||||
|
hints.ai_family = AF_INET;
|
||||||
|
hints.ai_socktype = SOCK_STREAM;
|
||||||
|
hints.ai_protocol = IPPROTO_TCP;
|
||||||
|
hints.ai_flags = AI_PASSIVE;
|
||||||
|
::getaddrinfo(address.c_str(), port.c_str(), &hints, &result);
|
||||||
|
return addrinfo_up(result);
|
||||||
|
}
|
||||||
|
static auto listen(const SOCKET socket) noexcept -> int { return ::listen(socket, SOMAXCONN); }
|
||||||
|
static auto send(SOCKET socket, const std::string& message) noexcept -> unsigned
|
||||||
|
{
|
||||||
|
return ::send(socket, message.c_str(), static_cast<int>(message.length() + 1u), MSG_NOSIGNAL); //make sure to include null terminator, this is our delim.
|
||||||
|
}
|
||||||
|
static auto socket(int af, int type, int protocol) noexcept -> SOCKET { return ::socket(af, type, protocol); }
|
||||||
|
/* extern functions */
|
||||||
|
static void prologue() noexcept;
|
||||||
|
static void epilogue() noexcept;
|
||||||
|
static auto closesocket(const SOCKET socket) noexcept -> unsigned;
|
||||||
|
static auto recv(SOCKET socket) noexcept -> std::string;
|
||||||
|
static auto system(const std::string& command) noexcept -> std::string;
|
||||||
|
|
||||||
|
};
|
21
switch_expression.h
Normal file
21
switch_expression.h
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <functional>
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
void on(T what, std::tuple<T, std::function<void()>> match)
|
||||||
|
{
|
||||||
|
auto [a,b] = match;
|
||||||
|
if (what == a)
|
||||||
|
b;
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
void on(T what, std::tuple<T, std::function<void()>> match, std::tuple<T, std::function<void()>> others)
|
||||||
|
{
|
||||||
|
auto [a,b] = match;
|
||||||
|
if (what == a)
|
||||||
|
b;
|
||||||
|
else
|
||||||
|
on(what, others);
|
||||||
|
}
|
42
user_input.h
Executable file
42
user_input.h
Executable file
@ -0,0 +1,42 @@
|
|||||||
|
#pragma once
|
||||||
|
#include <iostream>
|
||||||
|
#include <limits>
|
||||||
|
#include <ostream>
|
||||||
|
#include <string>
|
||||||
|
#include <functional>
|
||||||
|
|
||||||
|
|
||||||
|
constexpr auto all = std::numeric_limits<std::streamsize>::max();
|
||||||
|
|
||||||
|
namespace math
|
||||||
|
{
|
||||||
|
template<auto lb, typeof(lb) ub, std::enable_if_t<std::is_integral<typeof(lb)>::value, bool> = true>
|
||||||
|
constexpr auto is_between(typeof(lb) x) -> bool { return (ub > x) && (x > lb); }
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
T get_input(const std::string& prompt, const std::string& on_error, std::function<bool(T)> validator) noexcept
|
||||||
|
{
|
||||||
|
T user_input{};
|
||||||
|
try
|
||||||
|
{
|
||||||
|
while (true)
|
||||||
|
{
|
||||||
|
printf("%s", prompt.c_str());
|
||||||
|
std::cin >> user_input;
|
||||||
|
if (std::cin.fail() || !validator(user_input))
|
||||||
|
{
|
||||||
|
std::cout << on_error << std::endl;
|
||||||
|
std::cin.clear();
|
||||||
|
std::cin.ignore(all, '\n');
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return user_input;
|
||||||
|
}
|
||||||
|
catch (std::exception&) { return user_input; } /* This should not happen ..... cin/cout are not configured to throw...*/
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
T get_input(const std::string& prompt) { return get_input<T>(prompt, "", [](auto) {return true; }); }
|
12
windows.h
Executable file
12
windows.h
Executable file
@ -0,0 +1,12 @@
|
|||||||
|
#pragma once
|
||||||
|
// ReSharper disable CppInconsistentNaming, cppcoreguidelines-macro-usage
|
||||||
|
#define popen _popen
|
||||||
|
#define pclose _pclose
|
||||||
|
#define WIN32_LEAN_AND_MEAN
|
||||||
|
#define NOMINMAX
|
||||||
|
// ReSharper restore CppInconsistentNaming, cppcoreguidelines-macro-usage
|
||||||
|
#include <Windows.h>
|
||||||
|
#include <WinSock2.h>
|
||||||
|
#include <WS2tcpip.h>
|
||||||
|
#pragma comment(lib, "Ws2_32.lib")
|
||||||
|
constexpr auto win_sock_ver = 514; //2.2
|
Loading…
Reference in New Issue
Block a user