tglane / socketwrapper Goto Github PK
View Code? Open in Web Editor NEWAsync/Sync networking library including UDP, TCP and TLS/TCP socket classes written in C++ 17 that utilizes epoll and kqueue as backend.
License: MIT License
Async/Sync networking library including UDP, TCP and TLS/TCP socket classes written in C++ 17 that utilizes epoll and kqueue as backend.
License: MIT License
$ make
clang -o tls_example tls_example.cpp -std=c++17 -fpic -Wall -Werror -Wextra -pedantic -lpthread -lstdc++ -lssl -lcrypto
[...]
/usr/bin/../lib/gcc/i686-linux-gnu/8/../../../../include/c++/8/optional:200:11: error: destructor called on
non-final 'net::tls_connection<net::ip_version::v4>' that has virtual functions but non-virtual destructor
[...]
1 error generated.
make: *** [Makefile:14: tls] Fehler 1
tells that the base class constructor is not virtual which might cause memleaks.
Easy to fix with
diff --git a/include/socketwrapper/detail/base_socket.hpp b/include/socketwrapper/detail/base_socket.hpp
index 230cde9..f575bd3 100644
--- a/include/socketwrapper/detail/base_socket.hpp
+++ b/include/socketwrapper/detail/base_socket.hpp
@@ -57,7 +57,7 @@ public:
#endif
}
- ~base_socket()
+ virtual ~base_socket()
{
if(m_sockfd > 0)
{
To build using clang I used
diff --git a/example/Makefile b/example/Makefile
index cf6996a..56322d5 100644
--- a/example/Makefile
+++ b/example/Makefile
@@ -1,7 +1,7 @@
-cc = g++
+cc = clang
CFLAGS = -std=c++17 -fpic -Wall -Werror -Wextra -pedantic
-LDFLAGS = -lpthread
+LDFLAGS = -lpthread -lstdc++
%.o: %.cpp
$(cc) -c $< -o $@ $(CFLAGS)
Hi,
I see frequent blocks of duplicate code within constexpr conditions.
You might consider trimming those using a traits class that distinguishes between the two different cases of IPv4 and IPv6:
diff --git a/include/socketwrapper/detail/utility.hpp b/include/socketwrapper/detail/utility.hpp
index 5de451b..e49da53 100644
--- a/include/socketwrapper/detail/utility.hpp
+++ b/include/socketwrapper/detail/utility.hpp
@@ -38,6 +38,23 @@ struct connection_info
namespace detail {
+template<ip_version IP_VER>
+struct traits;
+
+template<>
+struct traits<ip_version::v4>
+{
+ using addr = sockaddr_in;
+ static constexpr size_t addr_str_len = INET_ADDRSTRLEN;
+};
+
+template<>
+struct traits<ip_version::v6>
+{
+ using addr = sockaddr_in6;
+ static constexpr size_t addr_str_len = INET6_ADDRSTRLEN;
+};
+
template<ip_version IP_VER>
inline int resolve_hostname(std::string_view host_name, uint16_t port, socket_type type, std::variant<sockaddr_in, sockaddr_in6>& addr_out)
{
This will e.g. be used to trim this:
template<ip_version IP_VER>
inline connection_info resolve_addrinfo(sockaddr* addr_in)
{
connection_info peer {};
if constexpr(IP_VER == ip_version::v4)
{
peer.addr.resize(INET_ADDRSTRLEN);
std::string port_str; // Use string instead of array here because std::stoi creates a string anyway
port_str.resize(6);
if(inet_ntop(AF_INET, &(reinterpret_cast<sockaddr_in*>(addr_in)->sin_addr), peer.addr.data(), peer.addr$
throw std::runtime_error {"Failed to resolve addrinfo."};
peer.port = ntohs(reinterpret_cast<sockaddr_in*>(addr_in)->sin_port);
return peer;
}
else if constexpr(IP_VER == ip_version::v6)
{
peer.addr.resize(INET6_ADDRSTRLEN);
std::string port_str; // Use string instead of array here because std::stoi creates a string anyway
port_str.resize(6);
if(inet_ntop(AF_INET, &(reinterpret_cast<sockaddr_in6*>(addr_in)->sin6_addr), peer.addr.data(), peer.ad$
throw std::runtime_error {"Failed to resolve addrinfo."};
peer.port = ntohs(reinterpret_cast<sockaddr_in6*>(addr_in)->sin6_port);
return peer;
}
else
{
static_assert(IP_VER == ip_version::v4 || IP_VER == ip_version::v6);
}
}
Into just that:
template<ip_version IP_VER>
inline connection_info resolve_addrinfo(sockaddr* addr_in)
{
connection_info peer {};
peer.addr.resize(traits<IP_VER>::addr_str_len);
std::string port_str; // Use string instead of array here because std::stoi creates a string anyway
port_str.resize(6);
if(inet_ntop(AF_INET, &(reinterpret_cast<typename traits<IP_VER>::addr*>(addr_in)->sin_addr), peer.addr.dat$
throw std::runtime_error {"Failed to resolve addrinfo."};
peer.port = ntohs(reinterpret_cast<typename traits<IP_VER>::addr*>(addr_in)->sin_port);
return peer;
}
Of course this works elegantly only if there are no differences except types and values used in the distinction. Also you exchange the nice compile-time error of the static_assert for a less helpful C++ template error.
Currently only the sync operations (read, write, accept) offer the option to set a timeout after which the operation returns even if no data was processed. This should also be possible for the async counterparts.
Currently all registered async callbacks will be invalidated Ehen the socket instance is moved.
After this is fixed a moved socket should keep all registered callback after being moved.
Replace most exceptions either by a result type or simple error codes.
All of the socket types share some basic data type like the file descriptor or the network family.
This would be nice to store multiple socket types in one container even if they do not share the same signatures for their read/write methods.
Hey, where could I contact you in private about your project and some related stuff ?
Currently the only way to set configuration settings of a socket is to use the sockets base_socket::get()
function to receive the file descriptor and use it with POSIXs functions to configure the socket. There should be member functions that allow the user to do this.
Currently all async operations are executed by the implemented class async_context
s singleton instance. I want to add the possibility to change the async "executor" for each socket instance seperately to allow users more flexible use of async send/receive functions.
Currently all functions that use net::span
as buffer parameter take it as a r-value reference. This should be changed to a value type parameter because it is a non-owning cheap to copy type --- like with std::string_view
as a parameter.
With C++20 came the coroutine library to support concurrent operations. This is particularly interesting for IO operations like we have in networking.
The goal should be to have read/write/accept
functions for each socket class that return an awaitable. Internally the executor/notifier implementations should be used.
I will start with some research on this topic since the C++ coroutines seem rather complicated.
Hello changed your code so that one receiver stays live to accept calls and sender periodically sends data. Single sender, single receiver it works fine but when i run second sender the code gives runtime error and exits. Maybe I need to add some more code to example to make it working?
#include "../socketwrapper.hpp"
#include
#include
#include
int main(int argc, char**argv)
{
if(argc <= 1)
return 0;
if(strcmp(argv[1], "r") == 0)
{
std::cout << "--- Receiver ---\n";
/* net::tcp_acceptor<net::ip_version::v4> acceptor_two {"0.0.0.0", 4556};
acceptor_two.async_accept([](net::tcp_connection<net::ip_version::v4>&& sock) {
std::cout << sock.get() << std::endl;
});
*/
net::tcp_acceptornet::ip_version::v4 acceptor {"0.0.0.0", 5060};
std::cout << "Waiting for accept\n";
std::vector<net::tcp_connectionnet::ip_version::v4> conns;
acceptor.async_accept([&conns](net::tcp_connectionnet::ip_version::v4&& conn) {
std::array<char, 1024> buffer;
std::cout << "Accepted\n";
conns.push_back(std::move(conn));
auto& sock = conns.back();
sock.async_read(net::span {buffer}, [&buffer](size_t br){
std::cout << "Received: " << br << " - " << std::string_view {buffer.data(), br} << '\n';
});
});
std::cout << "Wait for data ...\n";
std::this_thread::sleep_for(std::chrono::milliseconds(100000));
}
else if(strcmp(argv[1], "s") == 0)
{
{
std::cout << "--- Sender ---\n";
net::tcp_connection<net::ip_version::v4> sock {"127.0.0.1", 5060};
std::cout << "Connected\n";
std::vector<char> vec {'H', 'e', 'l', 'l', 'o'};
std::this_thread::sleep_for(std::chrono::milliseconds(2000));
std::string_view buffer {"Hello String_view-World"};
sock.send(net::span {buffer.begin(), buffer.end()});
std::cout << "Sent\n";
}
{
std::cout << "--- Sender ---\n";
net::tcp_connection<net::ip_version::v4> sock {"127.0.0.1", 5060};
std::cout << "Connected\n";
std::vector<char> vec {'H', 'e', 'l', 'l', 'o'};
// sock.send(net::span {vec});
// sock.send(net::span {std::string {"Hello World"}});
while(true){
std::this_thread::sleep_for(std::chrono::milliseconds(500));
std::string_view buffer {"Hello String_view -World"};
sock.send(net::span {buffer.begin(), buffer.end()});
}
std::cout << "Sent\n";
}
}
}
Call read/write/accept on a socket and receive a std::future
that is resolved when the async socket operations finished inside the async context. This avoids the clutter that can be caused by a multitude of callbacks.
Currently only one callback per socket is registered so you cant register a write and a read callback at the same time.
It should be possible to register one read and one write callback per socket at the same time.
By calling a async read/write function after a read/write callback was already registered should overwrite the previously registered read/write callback.
Results in no decryption and failed handshakes.
Currently the only "abstraction" for ip addresses and ports to represent an endpoint is a std::string_view
storing the ip address of the endpoint as a string and a uint16_t
that represents the endpoints port.
This should be abstracted away by a address/endpoint class that offers multiple options to create an instance of it.
Just a little heads up I'm getting this warning in Visual Studio Code on Ubuntu 20.04 compiling with GCC 11
And I'm using Cmake to include the header only library as a INTERFACE
I have tried to clone a brand new up to date version of your repo and it still gives me the warning
Btw I do store all external libraries in the project folder in Libraries/External for easier management and that is why the path is starting with that
[build] Libraries/External/Cpp-socketwrapper/include/socketwrapper/detail/async.hpp: In member function ‘bool net::detail::async_context::add(int, net::detail::async_context::event_type, CALLBACK_TYPE&&)’:
[build] Libraries/External/Cpp-socketwrapper/include/socketwrapper/detail/async.hpp:125:38: warning: bitwise operation between different enumeration types ‘const net::detail::async_context::event_type’ and ‘EPOLL_EVENTS’ is deprecated [-Wdeprecated-enum-enum-conversion]
[build] 125 | item.event.events = type | EPOLLET;
[build] | ^
And noticed you had made a issue about a month ago about async so were starting to think it were related wich is why I wanted to inform you
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.