From 1e2c5662958502f518480b8f87a347e5d3f6adea Mon Sep 17 00:00:00 2001 From: Michele Caini Date: Tue, 28 Nov 2017 14:13:16 +0100 Subject: [PATCH] improved low-level interface for tcp/udp/dns --- src/uvw/dns.hpp | 40 ++++++++--- src/uvw/tcp.hpp | 6 +- src/uvw/udp.hpp | 183 +++++++++++++++++++++++++++++++++++------------- 3 files changed, 171 insertions(+), 58 deletions(-) diff --git a/src/uvw/dns.hpp b/src/uvw/dns.hpp index b823f439..7b8e91f7 100644 --- a/src/uvw/dns.hpp +++ b/src/uvw/dns.hpp @@ -201,6 +201,15 @@ class GetNameInfoReq final: public Request { public: using Request::Request; + /** + * @brief Async [getnameinfo](http://linux.die.net/man/3/getnameinfo). + * @param addr Initialized `sockaddr_in` or `sockaddr_in6` data structure. + * @param flags Optional flags that modify the behavior of `getnameinfo`. + */ + void nameInfo(const sockaddr &addr, int flags = 0) { + invoke(&uv_getnameinfo, parent(), get(), &nameInfoCallback, &addr, flags); + } + /** * @brief Async [getnameinfo](http://linux.die.net/man/3/getnameinfo). * @param ip A valid IP address. @@ -211,8 +220,7 @@ public: void nameInfo(std::string ip, unsigned int port, int flags = 0) { typename details::IpTraits::Type addr; details::IpTraits::addrFunc(ip.data(), port, &addr); - auto saddr = reinterpret_cast(&addr); - invoke(&uv_getnameinfo, parent(), get(), &nameInfoCallback, saddr, flags); + nameInfo(reinterpret_cast(addr), flags); } /** @@ -222,7 +230,26 @@ public: */ template void nameInfo(Addr addr, int flags = 0) { - nameInfo(addr.ip, addr.port, flags); + nameInfo(std::move(addr.ip), addr.port, flags); + } + + /** + * @brief Sync [getnameinfo](http://linux.die.net/man/3/getnameinfo). + * + * @param addr Initialized `sockaddr_in` or `sockaddr_in6` data structure. + * @param flags Optional flags that modify the behavior of `getnameinfo`. + * + * @return A `std::pair` composed as it follows: + * * A boolean value that is true in case of success, false otherwise. + * * A `std::pair` composed as it follows: + * * A `const char *` containing a valid hostname. + * * A `const char *` containing a valid service name. + */ + std::pair> + nameInfoSync(const sockaddr &addr, int flags = 0) { + auto req = get(); + auto err = uv_getnameinfo(parent(), req, nullptr, &addr, flags); + return std::make_pair(!err, std::make_pair(req->host, req->service)); } /** @@ -243,10 +270,7 @@ public: nameInfoSync(std::string ip, unsigned int port, int flags = 0) { typename details::IpTraits::Type addr; details::IpTraits::addrFunc(ip.data(), port, &addr); - auto req = get(); - auto saddr = reinterpret_cast(&addr); - auto err = uv_getnameinfo(parent(), req, nullptr, saddr, flags); - return std::make_pair(!err, std::make_pair(req->host, req->service)); + return nameInfoSync(reinterpret_cast(addr), flags); } /** @@ -264,7 +288,7 @@ public: template std::pair> nameInfoSync(Addr addr, int flags = 0) { - return nameInfoSync(addr.ip, addr.port, flags); + return nameInfoSync(std::move(addr.ip), addr.port, flags); } }; diff --git a/src/uvw/tcp.hpp b/src/uvw/tcp.hpp index dfeeeca3..6f5639de 100644 --- a/src/uvw/tcp.hpp +++ b/src/uvw/tcp.hpp @@ -130,7 +130,7 @@ public: * @param opts Optional additional flags. */ void bind(const sockaddr &addr, Flags opts = Flags{}) { - invoke(&uv_tcp_bind, get(), reinterpret_cast(&addr), opts); + invoke(&uv_tcp_bind, get(), &addr, opts); } /** @@ -175,7 +175,7 @@ public: */ template void bind(Addr addr, Flags opts = Flags{}) { - bind(addr.ip, addr.port, opts); + bind(std::move(addr.ip), addr.port, std::move(opts)); } /** @@ -244,7 +244,7 @@ public: */ template void connect(Addr addr) { - connect(addr.ip, addr.port); + connect(std::move(addr.ip), addr.port); } private: diff --git a/src/uvw/udp.hpp b/src/uvw/udp.hpp index 88d48c90..eccb0f41 100644 --- a/src/uvw/udp.hpp +++ b/src/uvw/udp.hpp @@ -157,6 +157,25 @@ public: invoke(&uv_udp_open, get(), socket); } + /** + * @brief Binds the UDP handle to an IP address and port. + * + * Available flags are: + * + * * `UDPHandle::Bind::IPV6ONLY` + * * `UDPHandle::Bind::REUSEADDR` + * + * See the official + * [documentation](http://docs.libuv.org/en/v1.x/udp.html#c.uv_udp_flags) + * for further details. + * + * @param addr Initialized `sockaddr_in` or `sockaddr_in6` data structure. + * @param opts Optional additional flags. + */ + void bind(const sockaddr &addr, Flags opts = Flags{}) { + invoke(&uv_udp_bind, get(), &addr, opts); + } + /** * @brief Binds the UDP handle to an IP address and port. * @@ -177,7 +196,7 @@ public: void bind(std::string ip, unsigned int port, Flags opts = Flags{}) { typename details::IpTraits::Type addr; details::IpTraits::addrFunc(ip.data(), port, &addr); - invoke(&uv_udp_bind, get(), reinterpret_cast(&addr), opts); + bind(reinterpret_cast(addr), std::move(opts)); } /** @@ -197,7 +216,7 @@ public: */ template void bind(Addr addr, Flags opts = Flags{}) { - bind(addr.ip, addr.port, opts); + bind(std::move(addr.ip), addr.port, std::move(opts)); } /** @@ -289,16 +308,11 @@ public: * A SendEvent event will be emitted when the data have been sent.
* An ErrorEvent event will be emitted in case of errors. * - * @param ip The address to which to send data. - * @param port The port to which to send data. + * @param addr Initialized `sockaddr_in` or `sockaddr_in6` data structure. * @param data The data to be sent. * @param len The lenght of the submitted data. */ - template - void send(std::string ip, unsigned int port, std::unique_ptr data, unsigned int len) { - typename details::IpTraits::Type addr; - details::IpTraits::addrFunc(ip.data(), port, &addr); - + void send(const sockaddr &addr, std::unique_ptr data, unsigned int len) { auto req = loop().resource( std::unique_ptr{ data.release(), [](char *ptr) { delete[] ptr; } @@ -310,7 +324,32 @@ public: req->once(listener); req->once(listener); - req->send(get(), reinterpret_cast(&addr)); + req->send(get(), &addr); + } + + /** + * @brief Sends data over the UDP socket. + * + * Note that if the socket has not previously been bound with `bind()`, it + * will be bound to `0.0.0.0` (the _all interfaces_ IPv4 address) and a + * random port number. + * + * The handle takes the ownership of the data and it is in charge of delete + * them. + * + * A SendEvent event will be emitted when the data have been sent.
+ * An ErrorEvent event will be emitted in case of errors. + * + * @param ip The address to which to send data. + * @param port The port to which to send data. + * @param data The data to be sent. + * @param len The lenght of the submitted data. + */ + template + void send(std::string ip, unsigned int port, std::unique_ptr data, unsigned int len) { + typename details::IpTraits::Type addr; + details::IpTraits::addrFunc(ip.data(), port, &addr); + send(reinterpret_cast(addr), std::move(data), len); } /** @@ -332,7 +371,39 @@ public: */ template void send(Addr addr, std::unique_ptr data, unsigned int len) { - send(addr.ip, addr.port, std::move(data), len); + send(std::move(addr.ip), addr.port, std::move(data), len); + } + + /** + * @brief Sends data over the UDP socket. + * + * Note that if the socket has not previously been bound with `bind()`, it + * will be bound to `0.0.0.0` (the _all interfaces_ IPv4 address) and a + * random port number. + * + * The handle doesn't take the ownership of the data. Be sure that their + * lifetime overcome the one of the request. + * + * A SendEvent event will be emitted when the data have been sent.
+ * An ErrorEvent event will be emitted in case of errors. + * + * @param addr Initialized `sockaddr_in` or `sockaddr_in6` data structure. + * @param data The data to be sent. + * @param len The lenght of the submitted data. + */ + void send(const sockaddr &addr, char *data, unsigned int len) { + auto req = loop().resource( + std::unique_ptr{ + data, [](char *) {} + }, len); + + auto listener = [ptr = shared_from_this()](const auto &event, const auto &) { + ptr->publish(event); + }; + + req->once(listener); + req->once(listener); + req->send(get(), &addr); } /** @@ -357,19 +428,7 @@ public: void send(std::string ip, unsigned int port, char *data, unsigned int len) { typename details::IpTraits::Type addr; details::IpTraits::addrFunc(ip.data(), port, &addr); - - auto req = loop().resource( - std::unique_ptr{ - data, [](char *) {} - }, len); - - auto listener = [ptr = shared_from_this()](const auto &event, const auto &) { - ptr->publish(event); - }; - - req->once(listener); - req->once(listener); - req->send(get(), reinterpret_cast(&addr)); + send(reinterpret_cast(addr), data, len); } /** @@ -391,7 +450,31 @@ public: */ template void send(Addr addr, char *data, unsigned int len) { - send(addr.ip, addr.port, data, len); + send(std::move(addr.ip), addr.port, data, len); + } + + /** + * @brief Sends data over the UDP socket. + * + * Same as `send()`, but it won’t queue a send request if it can’t be + * completed immediately. + * + * @param addr Initialized `sockaddr_in` or `sockaddr_in6` data structure. + * @param data The data to be sent. + * @param len The lenght of the submitted data. + * @return Number of bytes written. + */ + template + int trySend(const sockaddr &addr, std::unique_ptr data, unsigned int len) { + uv_buf_t bufs[] = { uv_buf_init(data.get(), len) }; + auto bw = uv_udp_try_send(get(), bufs, 1, &addr); + + if(bw < 0) { + publish(ErrorEvent{bw}); + bw = 0; + } + + return bw; } /** @@ -410,16 +493,7 @@ public: int trySend(std::string ip, unsigned int port, std::unique_ptr data, unsigned int len) { typename details::IpTraits::Type addr; details::IpTraits::addrFunc(ip.data(), port, &addr); - - uv_buf_t bufs[] = { uv_buf_init(data.get(), len) }; - auto bw = uv_udp_try_send(get(), bufs, 1, reinterpret_cast(&addr)); - - if(bw < 0) { - publish(ErrorEvent{bw}); - bw = 0; - } - - return bw; + return trySend(reinterpret_cast(addr), std::move(data), len); } /** @@ -435,7 +509,31 @@ public: */ template int trySend(Addr addr, std::unique_ptr data, unsigned int len) { - return trySend(addr.ip, addr.port, std::move(data), len); + return trySend(std::move(addr.ip), addr.port, std::move(data), len); + } + + /** + * @brief Sends data over the UDP socket. + * + * Same as `send()`, but it won’t queue a send request if it can’t be + * completed immediately. + * + * @param addr Initialized `sockaddr_in` or `sockaddr_in6` data structure. + * @param data The data to be sent. + * @param len The lenght of the submitted data. + * @return Number of bytes written. + */ + template + int trySend(const sockaddr &addr, char *data, unsigned int len) { + uv_buf_t bufs[] = { uv_buf_init(data, len) }; + auto bw = uv_udp_try_send(get(), bufs, 1, &addr); + + if(bw < 0) { + publish(ErrorEvent{bw}); + bw = 0; + } + + return bw; } /** @@ -454,16 +552,7 @@ public: int trySend(std::string ip, unsigned int port, char *data, unsigned int len) { typename details::IpTraits::Type addr; details::IpTraits::addrFunc(ip.data(), port, &addr); - - uv_buf_t bufs[] = { uv_buf_init(data, len) }; - auto bw = uv_udp_try_send(get(), bufs, 1, reinterpret_cast(&addr)); - - if(bw < 0) { - publish(ErrorEvent{bw}); - bw = 0; - } - - return bw; + return trySend(reinterpret_cast(addr), data, len); } /** @@ -479,7 +568,7 @@ public: */ template int trySend(Addr addr, char *data, unsigned int len) { - return trySend(addr.ip, addr.port, data, len); + return trySend(std::move(addr.ip), addr.port, data, len); } /**