1 #pragma once 2 3 4 #include <type_traits> 5 #include <algorithm> 6 #include <iterator> 7 #include <utility> 8 #include <cstddef> 9 #include <memory> 10 #include <string> 11 #include <uv.h> 12 #include "request.hpp" 13 #include "handle.hpp" 14 #include "util.hpp" 15 16 17 namespace uvw { 18 19 20 /** 21 * @brief SendEvent event. 22 * 23 * It will be emitted by UDPHandle according with its functionalities. 24 */ 25 struct SendEvent {}; 26 27 28 /** 29 * @brief UDPDataEvent event. 30 * 31 * It will be emitted by UDPHandle according with its functionalities. 32 */ 33 struct UDPDataEvent { UDPDataEventuvw::UDPDataEvent34 explicit UDPDataEvent(Addr sndr, std::unique_ptr<const char[]> buf, std::size_t len, bool part) noexcept 35 : data{std::move(buf)}, length{len}, sender{std::move(sndr)}, partial{part} 36 {} 37 38 std::unique_ptr<const char[]> data; /*!< A bunch of data read on the stream. */ 39 std::size_t length; /*!< The amount of data read on the stream. */ 40 Addr sender; /*!< A valid instance of Addr. */ 41 bool partial; /*!< True if the message was truncated, false otherwise. */ 42 }; 43 44 45 namespace details { 46 47 48 enum class UVUdpFlags: std::underlying_type_t<uv_udp_flags> { 49 IPV6ONLY = UV_UDP_IPV6ONLY, 50 REUSEADDR = UV_UDP_REUSEADDR 51 }; 52 53 54 enum class UVMembership: std::underlying_type_t<uv_membership> { 55 LEAVE_GROUP = UV_LEAVE_GROUP, 56 JOIN_GROUP = UV_JOIN_GROUP 57 }; 58 59 60 class SendReq final: public Request<SendReq, uv_udp_send_t> { 61 public: 62 using Deleter = void(*)(char *); 63 SendReq(ConstructorAccess ca,std::shared_ptr<Loop> loop,std::unique_ptr<char[],Deleter> dt,unsigned int len)64 SendReq(ConstructorAccess ca, std::shared_ptr<Loop> loop, std::unique_ptr<char[], Deleter> dt, unsigned int len) 65 : Request<SendReq, uv_udp_send_t>{ca, std::move(loop)}, 66 data{std::move(dt)}, 67 buf{uv_buf_init(data.get(), len)} 68 {} 69 send(uv_udp_t * handle,const struct sockaddr * addr)70 void send(uv_udp_t *handle, const struct sockaddr* addr) { 71 invoke(&uv_udp_send, get(), handle, &buf, 1, addr, &defaultCallback<SendEvent>); 72 } 73 74 private: 75 std::unique_ptr<char[], Deleter> data; 76 uv_buf_t buf; 77 }; 78 79 80 } 81 82 83 /** 84 * @brief The UDPHandle handle. 85 * 86 * UDP handles encapsulate UDP communication for both clients and servers.<br/> 87 * By default, _IPv4_ is used as a template parameter. The handle already 88 * supports _IPv6_ out-of-the-box by using `uvw::IPv6`. 89 * 90 * To create an `UDPHandle` through a `Loop`, arguments follow: 91 * 92 * * An optional integer value that indicates optional flags used to initialize 93 * the socket. 94 * 95 * See the official 96 * [documentation](http://docs.libuv.org/en/v1.x/udp.html#c.uv_udp_init_ex) 97 * for further details. 98 */ 99 class UDPHandle final: public Handle<UDPHandle, uv_udp_t> { 100 template<typename I> recvCallback(uv_udp_t * handle,ssize_t nread,const uv_buf_t * buf,const sockaddr * addr,unsigned flags)101 static void recvCallback(uv_udp_t *handle, ssize_t nread, const uv_buf_t *buf, const sockaddr *addr, unsigned flags) { 102 const typename details::IpTraits<I>::Type *aptr = reinterpret_cast<const typename details::IpTraits<I>::Type *>(addr); 103 104 UDPHandle &udp = *(static_cast<UDPHandle*>(handle->data)); 105 // data will be destroyed no matter of what the value of nread is 106 std::unique_ptr<const char[]> data{buf->base}; 107 108 if(nread > 0) { 109 // data available (can be truncated) 110 udp.publish(UDPDataEvent{details::address<I>(aptr), std::move(data), static_cast<std::size_t>(nread), !(0 == (flags & UV_UDP_PARTIAL))}); 111 } else if(nread == 0 && addr == nullptr) { 112 // no more data to be read, doing nothing is fine 113 } else if(nread == 0 && addr != nullptr) { 114 // empty udp packet 115 udp.publish(UDPDataEvent{details::address<I>(aptr), std::move(data), static_cast<std::size_t>(nread), false}); 116 } else { 117 // transmission error 118 udp.publish(ErrorEvent(nread)); 119 } 120 } 121 122 public: 123 using Membership = details::UVMembership; 124 using Bind = details::UVUdpFlags; 125 using IPv4 = uvw::IPv4; 126 using IPv6 = uvw::IPv6; 127 128 using Handle::Handle; 129 UDPHandle(ConstructorAccess ca,std::shared_ptr<Loop> ref,unsigned int f)130 explicit UDPHandle(ConstructorAccess ca, std::shared_ptr<Loop> ref, unsigned int f) 131 : Handle{ca, std::move(ref)}, tag{FLAGS}, flags{f} 132 {} 133 134 /** 135 * @brief Initializes the handle. The actual socket is created lazily. 136 * @return True in case of success, false otherwise. 137 */ init()138 bool init() { 139 return (tag == FLAGS) 140 ? initialize(&uv_udp_init_ex, flags) 141 : initialize(&uv_udp_init); 142 } 143 144 /** 145 * @brief Opens an existing file descriptor or SOCKET as a UDP handle. 146 * 147 * The passed file descriptor or SOCKET is not checked for its type, but 148 * it’s required that it represents a valid datagram socket. 149 * 150 * See the official 151 * [documentation](http://docs.libuv.org/en/v1.x/udp.html#c.uv_udp_open) 152 * for further details. 153 * 154 * @param socket A valid socket handle (either a file descriptor or a SOCKET). 155 */ open(OSSocketHandle socket)156 void open(OSSocketHandle socket) { 157 invoke(&uv_udp_open, get(), socket); 158 } 159 160 /** 161 * @brief Binds the UDP handle to an IP address and port. 162 * 163 * Available flags are: 164 * 165 * * `UDPHandle::Bind::IPV6ONLY` 166 * * `UDPHandle::Bind::REUSEADDR` 167 * 168 * See the official 169 * [documentation](http://docs.libuv.org/en/v1.x/udp.html#c.uv_udp_flags) 170 * for further details. 171 * 172 * @param addr Initialized `sockaddr_in` or `sockaddr_in6` data structure. 173 * @param opts Optional additional flags. 174 */ bind(const sockaddr & addr,Flags<Bind> opts=Flags<Bind>{})175 void bind(const sockaddr &addr, Flags<Bind> opts = Flags<Bind>{}) { 176 invoke(&uv_udp_bind, get(), &addr, opts); 177 } 178 179 /** 180 * @brief Binds the UDP handle to an IP address and port. 181 * 182 * Available flags are: 183 * 184 * * `UDPHandle::Bind::IPV6ONLY` 185 * * `UDPHandle::Bind::REUSEADDR` 186 * 187 * See the official 188 * [documentation](http://docs.libuv.org/en/v1.x/udp.html#c.uv_udp_flags) 189 * for further details. 190 * 191 * @param ip The IP address to which to bind. 192 * @param port The port to which to bind. 193 * @param opts Optional additional flags. 194 */ 195 template<typename I = IPv4> bind(std::string ip,unsigned int port,Flags<Bind> opts=Flags<Bind>{})196 void bind(std::string ip, unsigned int port, Flags<Bind> opts = Flags<Bind>{}) { 197 typename details::IpTraits<I>::Type addr; 198 details::IpTraits<I>::addrFunc(ip.data(), port, &addr); 199 bind(reinterpret_cast<const sockaddr &>(addr), std::move(opts)); 200 } 201 202 /** 203 * @brief Binds the UDP handle to an IP address and port. 204 * 205 * Available flags are: 206 * 207 * * `UDPHandle::Bind::IPV6ONLY` 208 * * `UDPHandle::Bind::REUSEADDR` 209 * 210 * See the official 211 * [documentation](http://docs.libuv.org/en/v1.x/udp.html#c.uv_udp_flags) 212 * for further details. 213 * 214 * @param addr A valid instance of Addr. 215 * @param opts Optional additional flags. 216 */ 217 template<typename I = IPv4> bind(Addr addr,Flags<Bind> opts=Flags<Bind>{})218 void bind(Addr addr, Flags<Bind> opts = Flags<Bind>{}) { 219 bind<I>(std::move(addr.ip), addr.port, std::move(opts)); 220 } 221 222 /** 223 * @brief Get the local IP and port of the UDP handle. 224 * @return A valid instance of Addr, an empty one in case of errors. 225 */ 226 template<typename I = IPv4> sock() const227 Addr sock() const noexcept { 228 return details::address<I>(&uv_udp_getsockname, get()); 229 } 230 231 /** 232 * @brief Sets membership for a multicast address. 233 * 234 * Available values for `membership` are: 235 * 236 * * `UDPHandle::Membership::LEAVE_GROUP` 237 * * `UDPHandle::Membership::JOIN_GROUP` 238 * 239 * @param multicast Multicast address to set membership for. 240 * @param iface Interface address. 241 * @param membership Action to be performed. 242 * @return True in case of success, false otherwise. 243 */ 244 template<typename I = IPv4> multicastMembership(std::string multicast,std::string iface,Membership membership)245 bool multicastMembership(std::string multicast, std::string iface, Membership membership) { 246 return (0 == uv_udp_set_membership(get(), multicast.data(), iface.data(), static_cast<uv_membership>(membership))); 247 } 248 249 /** 250 * @brief Sets IP multicast loop flag. 251 * 252 * This makes multicast packets loop back to local sockets. 253 * 254 * @param enable True to enable multicast loop, false otherwise. 255 * @return True in case of success, false otherwise. 256 */ multicastLoop(bool enable=true)257 bool multicastLoop(bool enable = true) { 258 return (0 == uv_udp_set_multicast_loop(get(), enable)); 259 } 260 261 /** 262 * @brief Sets the multicast ttl. 263 * @param val A value in the range `[1, 255]`. 264 * @return True in case of success, false otherwise. 265 */ multicastTtl(int val)266 bool multicastTtl(int val) { 267 return (0 == uv_udp_set_multicast_ttl(get(), val > 255 ? 255 : val)); 268 } 269 270 /** 271 * @brief Sets the multicast interface to send or receive data on. 272 * @param iface Interface address. 273 * @return True in case of success, false otherwise. 274 */ 275 template<typename I = IPv4> multicastInterface(std::string iface)276 bool multicastInterface(std::string iface) { 277 return (0 == uv_udp_set_multicast_interface(get(), iface.data())); 278 } 279 280 /** 281 * @brief Sets broadcast on or off. 282 * @param enable True to set broadcast on, false otherwise. 283 * @return True in case of success, false otherwise. 284 */ broadcast(bool enable=false)285 bool broadcast(bool enable = false) { 286 return (0 == uv_udp_set_broadcast(get(), enable)); 287 } 288 289 /** 290 * @brief Sets the time to live. 291 * @param val A value in the range `[1, 255]`. 292 * @return True in case of success, false otherwise. 293 */ ttl(int val)294 bool ttl(int val) { 295 return (0 == uv_udp_set_ttl(get(), val > 255 ? 255 : val)); 296 } 297 298 /** 299 * @brief Sends data over the UDP socket. 300 * 301 * Note that if the socket has not previously been bound with `bind()`, it 302 * will be bound to `0.0.0.0` (the _all interfaces_ IPv4 address) and a 303 * random port number. 304 * 305 * The handle takes the ownership of the data and it is in charge of delete 306 * them. 307 * 308 * A SendEvent event will be emitted when the data have been sent.<br/> 309 * An ErrorEvent event will be emitted in case of errors. 310 * 311 * @param addr Initialized `sockaddr_in` or `sockaddr_in6` data structure. 312 * @param data The data to be sent. 313 * @param len The lenght of the submitted data. 314 */ send(const sockaddr & addr,std::unique_ptr<char[]> data,unsigned int len)315 void send(const sockaddr &addr, std::unique_ptr<char[]> data, unsigned int len) { 316 auto req = loop().resource<details::SendReq>( 317 std::unique_ptr<char[], details::SendReq::Deleter>{ 318 data.release(), [](char *ptr) { delete[] ptr; } 319 }, len); 320 321 auto listener = [ptr = shared_from_this()](const auto &event, const auto &) { 322 ptr->publish(event); 323 }; 324 325 req->once<ErrorEvent>(listener); 326 req->once<SendEvent>(listener); 327 req->send(get(), &addr); 328 } 329 330 /** 331 * @brief Sends data over the UDP socket. 332 * 333 * Note that if the socket has not previously been bound with `bind()`, it 334 * will be bound to `0.0.0.0` (the _all interfaces_ IPv4 address) and a 335 * random port number. 336 * 337 * The handle takes the ownership of the data and it is in charge of delete 338 * them. 339 * 340 * A SendEvent event will be emitted when the data have been sent.<br/> 341 * An ErrorEvent event will be emitted in case of errors. 342 * 343 * @param ip The address to which to send data. 344 * @param port The port to which to send data. 345 * @param data The data to be sent. 346 * @param len The lenght of the submitted data. 347 */ 348 template<typename I = IPv4> send(std::string ip,unsigned int port,std::unique_ptr<char[]> data,unsigned int len)349 void send(std::string ip, unsigned int port, std::unique_ptr<char[]> data, unsigned int len) { 350 typename details::IpTraits<I>::Type addr; 351 details::IpTraits<I>::addrFunc(ip.data(), port, &addr); 352 send(reinterpret_cast<const sockaddr &>(addr), std::move(data), len); 353 } 354 355 /** 356 * @brief Sends data over the UDP socket. 357 * 358 * Note that if the socket has not previously been bound with `bind()`, it 359 * will be bound to `0.0.0.0` (the _all interfaces_ IPv4 address) and a 360 * random port number. 361 * 362 * The handle takes the ownership of the data and it is in charge of delete 363 * them. 364 * 365 * A SendEvent event will be emitted when the data have been sent.<br/> 366 * An ErrorEvent event will be emitted in case of errors. 367 * 368 * @param addr A valid instance of Addr. 369 * @param data The data to be sent. 370 * @param len The lenght of the submitted data. 371 */ 372 template<typename I = IPv4> send(Addr addr,std::unique_ptr<char[]> data,unsigned int len)373 void send(Addr addr, std::unique_ptr<char[]> data, unsigned int len) { 374 send<I>(std::move(addr.ip), addr.port, std::move(data), len); 375 } 376 377 /** 378 * @brief Sends data over the UDP socket. 379 * 380 * Note that if the socket has not previously been bound with `bind()`, it 381 * will be bound to `0.0.0.0` (the _all interfaces_ IPv4 address) and a 382 * random port number. 383 * 384 * The handle doesn't take the ownership of the data. Be sure that their 385 * lifetime overcome the one of the request. 386 * 387 * A SendEvent event will be emitted when the data have been sent.<br/> 388 * An ErrorEvent event will be emitted in case of errors. 389 * 390 * @param addr Initialized `sockaddr_in` or `sockaddr_in6` data structure. 391 * @param data The data to be sent. 392 * @param len The lenght of the submitted data. 393 */ send(const sockaddr & addr,char * data,unsigned int len)394 void send(const sockaddr &addr, char *data, unsigned int len) { 395 auto req = loop().resource<details::SendReq>( 396 std::unique_ptr<char[], details::SendReq::Deleter>{ 397 data, [](char *) {} 398 }, len); 399 400 auto listener = [ptr = shared_from_this()](const auto &event, const auto &) { 401 ptr->publish(event); 402 }; 403 404 req->once<ErrorEvent>(listener); 405 req->once<SendEvent>(listener); 406 req->send(get(), &addr); 407 } 408 409 /** 410 * @brief Sends data over the UDP socket. 411 * 412 * Note that if the socket has not previously been bound with `bind()`, it 413 * will be bound to `0.0.0.0` (the _all interfaces_ IPv4 address) and a 414 * random port number. 415 * 416 * The handle doesn't take the ownership of the data. Be sure that their 417 * lifetime overcome the one of the request. 418 * 419 * A SendEvent event will be emitted when the data have been sent.<br/> 420 * An ErrorEvent event will be emitted in case of errors. 421 * 422 * @param ip The address to which to send data. 423 * @param port The port to which to send data. 424 * @param data The data to be sent. 425 * @param len The lenght of the submitted data. 426 */ 427 template<typename I = IPv4> send(std::string ip,unsigned int port,char * data,unsigned int len)428 void send(std::string ip, unsigned int port, char *data, unsigned int len) { 429 typename details::IpTraits<I>::Type addr; 430 details::IpTraits<I>::addrFunc(ip.data(), port, &addr); 431 send(reinterpret_cast<const sockaddr &>(addr), data, len); 432 } 433 434 /** 435 * @brief Sends data over the UDP socket. 436 * 437 * Note that if the socket has not previously been bound with `bind()`, it 438 * will be bound to `0.0.0.0` (the _all interfaces_ IPv4 address) and a 439 * random port number. 440 * 441 * The handle doesn't take the ownership of the data. Be sure that their 442 * lifetime overcome the one of the request. 443 * 444 * A SendEvent event will be emitted when the data have been sent.<br/> 445 * An ErrorEvent event will be emitted in case of errors. 446 * 447 * @param addr A valid instance of Addr. 448 * @param data The data to be sent. 449 * @param len The lenght of the submitted data. 450 */ 451 template<typename I = IPv4> send(Addr addr,char * data,unsigned int len)452 void send(Addr addr, char *data, unsigned int len) { 453 send<I>(std::move(addr.ip), addr.port, data, len); 454 } 455 456 /** 457 * @brief Sends data over the UDP socket. 458 * 459 * Same as `send()`, but it won’t queue a send request if it can’t be 460 * completed immediately. 461 * 462 * @param addr Initialized `sockaddr_in` or `sockaddr_in6` data structure. 463 * @param data The data to be sent. 464 * @param len The lenght of the submitted data. 465 * @return Number of bytes written. 466 */ 467 template<typename I = IPv4> trySend(const sockaddr & addr,std::unique_ptr<char[]> data,unsigned int len)468 int trySend(const sockaddr &addr, std::unique_ptr<char[]> data, unsigned int len) { 469 uv_buf_t bufs[] = { uv_buf_init(data.get(), len) }; 470 auto bw = uv_udp_try_send(get(), bufs, 1, &addr); 471 472 if(bw < 0) { 473 publish(ErrorEvent{bw}); 474 bw = 0; 475 } 476 477 return bw; 478 } 479 480 /** 481 * @brief Sends data over the UDP socket. 482 * 483 * Same as `send()`, but it won’t queue a send request if it can’t be 484 * completed immediately. 485 * 486 * @param ip The address to which to send data. 487 * @param port The port to which to send data. 488 * @param data The data to be sent. 489 * @param len The lenght of the submitted data. 490 * @return Number of bytes written. 491 */ 492 template<typename I = IPv4> trySend(std::string ip,unsigned int port,std::unique_ptr<char[]> data,unsigned int len)493 int trySend(std::string ip, unsigned int port, std::unique_ptr<char[]> data, unsigned int len) { 494 typename details::IpTraits<I>::Type addr; 495 details::IpTraits<I>::addrFunc(ip.data(), port, &addr); 496 return trySend(reinterpret_cast<const sockaddr &>(addr), std::move(data), len); 497 } 498 499 /** 500 * @brief Sends data over the UDP socket. 501 * 502 * Same as `send()`, but it won’t queue a send request if it can’t be 503 * completed immediately. 504 * 505 * @param addr A valid instance of Addr. 506 * @param data The data to be sent. 507 * @param len The lenght of the submitted data. 508 * @return Number of bytes written. 509 */ 510 template<typename I = IPv4> trySend(Addr addr,std::unique_ptr<char[]> data,unsigned int len)511 int trySend(Addr addr, std::unique_ptr<char[]> data, unsigned int len) { 512 return trySend<I>(std::move(addr.ip), addr.port, std::move(data), len); 513 } 514 515 /** 516 * @brief Sends data over the UDP socket. 517 * 518 * Same as `send()`, but it won’t queue a send request if it can’t be 519 * completed immediately. 520 * 521 * @param addr Initialized `sockaddr_in` or `sockaddr_in6` data structure. 522 * @param data The data to be sent. 523 * @param len The lenght of the submitted data. 524 * @return Number of bytes written. 525 */ 526 template<typename I = IPv4> trySend(const sockaddr & addr,char * data,unsigned int len)527 int trySend(const sockaddr &addr, char *data, unsigned int len) { 528 uv_buf_t bufs[] = { uv_buf_init(data, len) }; 529 auto bw = uv_udp_try_send(get(), bufs, 1, &addr); 530 531 if(bw < 0) { 532 publish(ErrorEvent{bw}); 533 bw = 0; 534 } 535 536 return bw; 537 } 538 539 /** 540 * @brief Sends data over the UDP socket. 541 * 542 * Same as `send()`, but it won’t queue a send request if it can’t be 543 * completed immediately. 544 * 545 * @param ip The address to which to send data. 546 * @param port The port to which to send data. 547 * @param data The data to be sent. 548 * @param len The lenght of the submitted data. 549 * @return Number of bytes written. 550 */ 551 template<typename I = IPv4> trySend(std::string ip,unsigned int port,char * data,unsigned int len)552 int trySend(std::string ip, unsigned int port, char *data, unsigned int len) { 553 typename details::IpTraits<I>::Type addr; 554 details::IpTraits<I>::addrFunc(ip.data(), port, &addr); 555 return trySend(reinterpret_cast<const sockaddr &>(addr), data, len); 556 } 557 558 /** 559 * @brief Sends data over the UDP socket. 560 * 561 * Same as `send()`, but it won’t queue a send request if it can’t be 562 * completed immediately. 563 * 564 * @param addr A valid instance of Addr. 565 * @param data The data to be sent. 566 * @param len The lenght of the submitted data. 567 * @return Number of bytes written. 568 */ 569 template<typename I = IPv4> trySend(Addr addr,char * data,unsigned int len)570 int trySend(Addr addr, char *data, unsigned int len) { 571 return trySend<I>(std::move(addr.ip), addr.port, data, len); 572 } 573 574 /** 575 * @brief Prepares for receiving data. 576 * 577 * Note that if the socket has not previously been bound with `bind()`, it 578 * is bound to `0.0.0.0` (the _all interfaces_ IPv4 address) and a random 579 * port number. 580 * 581 * An UDPDataEvent event will be emitted when the handle receives data.<br/> 582 * An ErrorEvent event will be emitted in case of errors. 583 */ 584 template<typename I = IPv4> recv()585 void recv() { 586 invoke(&uv_udp_recv_start, get(), &allocCallback, &recvCallback<I>); 587 } 588 589 /** 590 * @brief Stops listening for incoming datagrams. 591 */ stop()592 void stop() { 593 invoke(&uv_udp_recv_stop, get()); 594 } 595 596 /** 597 * @brief Gets the number of bytes queued for sending. 598 * 599 * It strictly shows how much information is currently queued. 600 * 601 * @return Number of bytes queued for sending. 602 */ sendQueueSize() const603 size_t sendQueueSize() const noexcept { 604 return uv_udp_get_send_queue_size(get()); 605 } 606 607 /** 608 * @brief Number of send requests currently in the queue awaiting to be processed. 609 * @return Number of send requests currently in the queue. 610 */ sendQueueCount() const611 size_t sendQueueCount() const noexcept { 612 return uv_udp_get_send_queue_count(get()); 613 } 614 615 private: 616 enum { DEFAULT, FLAGS } tag{DEFAULT}; 617 unsigned int flags{}; 618 }; 619 620 621 } 622