Keep It Simple Stupid NETwork - C++17 wrapping of your OS's native socket API
Keep It Simple Stupid NETwork.
The thought that led me to write this thing are better explained in this article
A lightweight, header only, crossplatform C++17 socket library.
Wrap all annoying C api calls to the OS inside a
sockettemplate class
stderr(without
iostream), and abort program
std::byte
send,
recv,
connect,
bind,
listen,
accept...
kissnet was desinged to be a slingle API for using low level tcp and udp sockets on any platform where you can run a modern C++ compiler. Kissnet currently supports
target_link_libraries(target network)with cmake, or -lnetwork in yoru compile command), networking will use the posix headers shiped with the OS, not the BeOS-like C++ network kit.
kissnet should probably work "as is" on other UNIX platform.
kissnet.hpp can be copied to your project's directory, or if you wish it can be installed and used from CMake as interface target
kissnet:
cmake -B build cmake --install build
Volontary contrived examples showing how the library looks like:
You can take a look a some of the programs in the
examplesdirectory.
#include #include #include#include <kissnet.hpp> using namespace std::chrono_literals; namespace kn = kissnet;
int main() {
/* No need to initializate SSL because its initializated at start of program if KISSNET_USE_OPENSSL is used. */ { //Create a kissnet tcp over ssl ipv4 socket kn::tcp_ssl_socket a_socket(kn::endpoint("cpz.github.io:443")); a_socket.connect(); //Create a "GET /" HTTP request, and send that packet into the socket auto get_index_request = std::string{ "GET / HTTP/1.1\r\nHost: cpz.github.io\r\n\r\n" }; //Send request a_socket.send(reinterpret_cast<const std::byte>(get_index_request.c_str()), get_index_request.size()); //Receive data into a buffer kn::buffer<4096> static_buffer; //Useless wait, just to show how long the response was std::this_thread::sleep_for(1s); //Print how much data our OS has for us std::cout << "bytes available to read : " << a_socket.bytes_available() << '\n'; //Get the data, and the lengh of data const auto [data_size, status_code] = a_socket.recv(static_buffer); //To print it as a good old C string, add a null terminator if(data_size < static_buffer.size()) static_buffer[data_size] = std::byte{ '\0' }; //Print the raw data as text into the terminal (should display html/css code here) std::cout << reinterpret_cast<const char>(static_buffer.data()) << '\n'; } /* Nothing need to do for OpenSSL uninitialization because our class will do everything by himself. */ /*No more socket here, this will actually close WSA on Windows*/ { //Create a kissnet tcp ipv4 socket kn::tcp_socket a_socket(kn::endpoint("avalon.ybalrid.info:80")); a_socket.connect(); //Create a "GET /" HTTP request, and send that packet into the socket auto get_index_request = std::string{ "GET / HTTP/1.1\r\nHost: avalon.ybalird.info\r\n\r\n" }; //Send request a_socket.send(reinterpret_cast<const std::byte>(get_index_request.c_str()), get_index_request.size()); //Receive data into a buffer kn::buffer<4096> static_buffer; //Useless wait, just to show how long the response was std::this_thread::sleep_for(1s); //Print how much data our OS has for us std::cout << "bytes available to read : " << a_socket.bytes_available() << '\n'; //Get the data, and the lengh of data const auto [data_size, status_code] = a_socket.recv(static_buffer); //To print it as a good old C string, add a null terminator if(data_size < static_buffer.size()) static_buffer[data_size] = std::byte{ '\0' }; //Print the raw data as text into the terminal (should display html/css code here) std::cout << reinterpret_cast<const char>(static_buffer.data()) << '\n'; } /*No more socket here, this will actually close WSA on Windows*/ { //Socket used to send, the "endpoint" is the destination of the data kn::udp_socket a_socket(kn::endpoint("127.0.0.1", 6666)); //Socket used to receive, the "endpoint" is where to listen to data kn::udp_socket b_socket(kn::endpoint("0.0.0.0", 6666)); b_socket.bind(); //Byte buffer kn::buffer<16> buff; //Build data to send (flat array of bytes for(unsigned char i = 0; i < 16; i++) buff[i] = std::byte{ i }; //Send data a_socket.send(buff.data(), 16); //Same deal as above std::this_thread::sleep_for(1s); //We do know, for the sake of the example, that there are 16 bytes to get from the network kn::buffer<16> recv_buff; //Actually print bytes_available std::cout << "avaliable in UDP socket : " << b_socket.bytes_available() << " bytes\n"; //You receive in the same way auto [received_bytes, status] = b_socket.recv(recv_buff); const auto from = b_socket.get_recv_endpoint(); //Print the data std::cout << "Received: "; for(unsigned char i = 0; i < 16; i++) { std::cout << std::hex << std::to_integer<int>(recv_buff[i]) << std::dec << ' '; } //Print who send the data std::cout << "From: " << from.address << ' ' << from.port << '\n'; } //So long, and thanks for all the fish return 0;
</kissnet.hpp>
#include #includenamespace kn = kissnet;
int main() { //setup socket kn::socket<:protocol::tcp> server(kn::endpoint("0.0.0.0:8080")); server.bind(); server.listen();
//Wait for one co auto client = server.accept(); //Read once in a 1k buffer kn::buffer<1024> buff; const auto [size, status] = client.recv(buff); //Add null terminator, and print as string if(size < buff.size()) buff[size] = std::byte{ 0 }; std::cout << reinterpret_cast<const char>(buff.data()) << '\n'; return 0;
}
</:protocol::tcp></kissnet.hpp>