LCOV - code coverage report
Current view: top level - snapwebsites - udp_client_server.cpp (source / functions) Hit Total Coverage
Test: coverage.info Lines: 1 155 0.6 %
Date: 2019-12-15 17:13:15 Functions: 2 16 12.5 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : // UDP Client & Server -- classes to ease handling sockets
       2             : // Copyright (c) 2012-2019  Made to Order Software Corp.  All Rights Reserved
       3             : //
       4             : // This program is free software; you can redistribute it and/or modify
       5             : // it under the terms of the GNU General Public License as published by
       6             : // the Free Software Foundation; either version 2 of the License, or
       7             : // (at your option) any later version.
       8             : //
       9             : // This program is distributed in the hope that it will be useful,
      10             : // but WITHOUT ANY WARRANTY; without even the implied warranty of
      11             : // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
      12             : // GNU General Public License for more details.
      13             : //
      14             : // You should have received a copy of the GNU General Public License
      15             : // along with this program; if not, write to the Free Software
      16             : // Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
      17             : 
      18             : 
      19             : 
      20             : // self
      21             : //
      22             : #include "snapwebsites/udp_client_server.h"
      23             : 
      24             : 
      25             : // snapwebsites lib
      26             : //
      27             : #include "snapwebsites/log.h"
      28             : 
      29             : 
      30             : // libaddr lib
      31             : //
      32             : #include <libaddr/iface.h>
      33             : 
      34             : 
      35             : // C++ lib
      36             : //
      37             : #include <sstream>
      38             : 
      39             : 
      40             : // C lib
      41             : //
      42             : #include <fcntl.h>
      43             : #include <net/if.h>
      44             : #include <netinet/in.h>
      45             : #include <netinet/ip.h>
      46             : #include <netinet/udp.h>
      47             : #include <poll.h>
      48             : #include <string.h>
      49             : #include <sys/ioctl.h>
      50             : #include <unistd.h>
      51             : 
      52             : 
      53             : // last include
      54             : //
      55             : #include <snapdev/poison.h>
      56             : 
      57             : 
      58             : 
      59             : 
      60             : namespace udp_client_server
      61             : {
      62             : 
      63             : // ========================= BASE =========================
      64             : 
      65             : /** \brief Initialize a UDP base object.
      66             :  *
      67             :  * This function initializes the UDP base object using the address and the
      68             :  * port as specified.
      69             :  *
      70             :  * The port is expected to be a host side port number (i.e. 59200).
      71             :  *
      72             :  * The \p addr parameter is a textual address. It may be an IPv4 or IPv6
      73             :  * address and it can represent a host name or an address defined with
      74             :  * just numbers. If the address cannot be resolved then an error occurs
      75             :  * and the constructor throws.
      76             :  *
      77             :  * \note
      78             :  * The socket is open in this process. If you fork() and exec() then the
      79             :  * socket gets closed by the operating system (i.e. close on exec()).
      80             :  *
      81             :  * \warning
      82             :  * We only make use of the first address found by getaddrinfo(). All
      83             :  * the other addresses are ignored.
      84             :  *
      85             :  * \todo
      86             :  * Add a constructor that supports a libaddr::addr object instead of
      87             :  * just a string address.
      88             :  *
      89             :  * \exception udp_client_server_parameter_error
      90             :  * The \p addr parameter is empty or the port is out of the supported range.
      91             :  *
      92             :  * \exception udp_client_server_runtime_error
      93             :  * The server could not be initialized properly. Either the address cannot be
      94             :  * resolved, the port is incompatible or not available, or the socket could
      95             :  * not be created.
      96             :  *
      97             :  * \param[in] addr  The address to convert to a numeric IP.
      98             :  * \param[in] port  The port number.
      99             :  * \param[in] family  The family used to search for 'addr'.
     100             :  */
     101           0 : udp_base::udp_base(std::string const & addr, int port, int family)
     102             :     : f_port(port)
     103           0 :     , f_addr(addr)
     104             : {
     105             :     // the address can't be an empty string
     106             :     //
     107           0 :     if(f_addr.empty())
     108             :     {
     109           0 :         throw udp_client_server_parameter_error("the address cannot be an empty string");
     110             :     }
     111             : 
     112             :     // the port must be between 0 and 65535
     113             :     // (although 0 won't work right as far as I know)
     114             :     //
     115           0 :     if(f_port < 0 || f_port >= 65536)
     116             :     {
     117           0 :         throw udp_client_server_parameter_error("invalid port for a client socket");
     118             :     }
     119             : 
     120             :     // for the getaddrinfo() function, convert the port to a string
     121             :     //
     122           0 :     std::stringstream decimal_port;
     123           0 :     decimal_port << f_port;
     124           0 :     std::string port_str(decimal_port.str());
     125             : 
     126             :     // define the getaddrinfo() hints
     127             :     // we are only interested by addresses representing datagrams and
     128             :     // acceptable by the UDP protocol
     129             :     //
     130           0 :     struct addrinfo hints = addrinfo();
     131           0 :     hints.ai_family = family;
     132           0 :     hints.ai_socktype = SOCK_DGRAM;
     133           0 :     hints.ai_protocol = IPPROTO_UDP;
     134             : 
     135             :     // retrieve the list of addresses defined by getaddrinfo()
     136             :     //
     137             :     struct addrinfo * info;
     138           0 :     int const r(getaddrinfo(addr.c_str(), port_str.c_str(), &hints, &info));
     139           0 :     if(r != 0 || info == nullptr)
     140             :     {
     141           0 :         throw udp_client_server_runtime_error(("invalid address or port: \"" + addr + ":" + port_str + "\"").c_str());
     142             :     }
     143           0 :     f_addrinfo = raii_addrinfo_t(info);
     144             : 
     145             :     // now create the socket with the very first socket family
     146             :     //
     147           0 :     f_socket.reset(socket(f_addrinfo->ai_family, SOCK_DGRAM | SOCK_CLOEXEC, IPPROTO_UDP));
     148           0 :     if(f_socket == nullptr)
     149             :     {
     150           0 :         throw udp_client_server_runtime_error(("could not create socket for: \"" + addr + ":" + port_str + "\"").c_str());
     151             :     }
     152           0 : }
     153             : 
     154             : 
     155             : /** \brief Retrieve a copy of the socket identifier.
     156             :  *
     157             :  * This function return the socket identifier as returned by the socket()
     158             :  * function. This can be used to change some flags.
     159             :  *
     160             :  * \return The socket used by this UDP client.
     161             :  */
     162           0 : int udp_base::get_socket() const
     163             : {
     164           0 :     return f_socket.get();
     165             : }
     166             : 
     167             : 
     168             : /** \brief Retrieve the size of the MTU on that connection.
     169             :  *
     170             :  * Linux offers a ioctl() function to retrieve the MTU's size. This
     171             :  * function uses that and returns the result. If the call fails,
     172             :  * then the function returns -1.
     173             :  *
     174             :  * The function returns the MTU's size of the socket on this side.
     175             :  * If you want to communicate effectively with another system, you
     176             :  * want to also ask about the MTU on the other side of the socket.
     177             :  *
     178             :  * \note
     179             :  * MTU stands for Maximum Transmission Unit.
     180             :  *
     181             :  * \note
     182             :  * PMTUD stands for Path Maximum Transmission Unit Discovery.
     183             :  *
     184             :  * \note
     185             :  * PLPMTU stands for Packetization Layer Path Maximum Transmission Unit
     186             :  * Discovery.
     187             :  *
     188             :  * \todo
     189             :  * We need to support the possibly dynamically changing MTU size
     190             :  * that the Internet may generate (or even a LAN if you let people
     191             :  * tweak their MTU "randomly".) This is done by preventing
     192             :  * defragmentation (see IP_NODEFRAG in `man 7 ip`) and also by
     193             :  * asking for MTU size discovery (IP_MTU_DISCOVER). The size
     194             :  * discovery changes over time as devices on the MTU path (the
     195             :  * route taken by the packets) changes over time. The idea is
     196             :  * to find the smallest MTU size of the MTU path and use that
     197             :  * to send packets of that size at the most. Note that packets
     198             :  * are otherwise automatically broken in smaller chunks and
     199             :  * rebuilt on the other side, but that is not efficient if you
     200             :  * expect to lose quite a few packets. The limit for chunked
     201             :  * packets is a little under 64Kb.
     202             :  *
     203             :  * \note
     204             :  * errno is either EBADF or set by ioctl().
     205             :  *
     206             :  * \sa
     207             :  * See `man 7 netdevice`
     208             :  *
     209             :  * \return -1 if the MTU could not be retrieved, the MTU's size otherwise.
     210             :  */
     211           0 : int udp_base::get_mtu_size() const
     212             : {
     213           0 :     if(f_socket != nullptr
     214           0 :     && f_mtu_size == 0)
     215             :     {
     216           0 :         addr::addr a;
     217           0 :         switch(f_addrinfo->ai_family)
     218             :         {
     219           0 :         case AF_INET:
     220           0 :             a.set_ipv4(*reinterpret_cast<struct sockaddr_in *>(f_addrinfo->ai_addr));
     221           0 :             break;
     222             : 
     223           0 :         case AF_INET6:
     224           0 :             a.set_ipv6(*reinterpret_cast<struct sockaddr_in6 *>(f_addrinfo->ai_addr));
     225           0 :             break;
     226             : 
     227           0 :         default:
     228           0 :             f_mtu_size = -1;
     229           0 :             errno = EBADF;
     230           0 :             break;
     231             : 
     232             :         }
     233           0 :         if(f_mtu_size == 0)
     234             :         {
     235           0 :             std::string iface_name;
     236           0 :             addr::iface::pointer_t i(find_addr_interface(a));
     237           0 :             if(i != nullptr)
     238             :             {
     239           0 :                 iface_name = i->get_name();
     240             :             }
     241             : 
     242           0 :             if(iface_name.empty())
     243             :             {
     244           0 :                 f_mtu_size = -1;
     245           0 :                 errno = EBADF;
     246             :             }
     247             :             else
     248             :             {
     249             :                 struct ifreq ifr;
     250           0 :                 memset(&ifr, 0, sizeof(ifr));
     251           0 :                 strncpy(ifr.ifr_name, iface_name.c_str(), sizeof(ifr.ifr_name));
     252           0 :                 if(ioctl(f_socket.get(), SIOCGIFMTU, &ifr) == 0)
     253             :                 {
     254           0 :                     f_mtu_size = ifr.ifr_mtu;
     255             :                 }
     256             :                 else
     257             :                 {
     258           0 :                     f_mtu_size = -1;
     259             :                     // errno -- defined by ioctl()
     260             :                 }
     261             :             }
     262             :         }
     263             :     }
     264             : 
     265           0 :     return f_mtu_size;
     266             : }
     267             : 
     268             : 
     269             : /** \brief Determine the size of the data buffer we can use.
     270             :  *
     271             :  * This function gets the MTU of the connection (i.e. not the PMTUD
     272             :  * or PLPMTUD yet...) and subtract the space necessary for the IP and
     273             :  * UDP headers. This is called the Maximum Segment Size (MSS).
     274             :  *
     275             :  * \todo
     276             :  * If the IP address (in f_addr) is an IPv6, then we need to switch to
     277             :  * the corresponding IPv6 subtractions.
     278             :  *
     279             :  * \todo
     280             :  * Look into the the IP options because some options add to the size
     281             :  * of the IP header. It's incredible that we have to take care of that
     282             :  * on our end!
     283             :  *
     284             :  * \todo
     285             :  * For congetion control, read more as described on ietf.org:
     286             :  * https://tools.ietf.org/html/rfc8085
     287             :  *
     288             :  * \todo
     289             :  * The sizes that will always work (as long as all the components of the
     290             :  * path are working as per the UDP RFC) are (1) for IPv4, 576 bytes, and
     291             :  * (2) for IPv6, 1280 bytes. This size is called EMTU_S which stands for
     292             :  * "Effective Maximum Transmission Unit for Sending."
     293             :  *
     294             :  * \return The size of the MMU, which is the MTU minus IP and UDP headers.
     295             :  */
     296           0 : int udp_base::get_mss_size() const
     297             : {
     298             :     // where these structures are defined
     299             :     //
     300             :     // ether_header -- /usr/include/net/ethernet.h
     301             :     // iphdr -- /usr/include/netinet/ip.h
     302             :     // udphdr -- /usr/include/netinet/udp.h
     303             :     //
     304           0 :     int const mtu(get_mtu_size()
     305             :             //- sizeof(ether_header)    // WARNING: this is for IPv4 only -- this is "transparent" to the MTU (i.e. it wraps the 1,500 bytes)
     306             :             //- ETHER_CRC_LEN           // this is the CRC for the ethernet which appears at the end of the packet
     307             :             - sizeof(iphdr)             // WARNING: this is for IPv4 only
     308             :             //- ...                     // the IP protocol accepts options!
     309           0 :             - sizeof(udphdr)
     310           0 :         );
     311             : 
     312           0 :     return mtu <= 0 ? -1 : mtu;
     313             : }
     314             : 
     315             : 
     316             : /** \brief Retrieve the port used by this UDP client.
     317             :  *
     318             :  * This function returns the port used by this UDP client. The port is
     319             :  * defined as an integer, host side.
     320             :  *
     321             :  * \return The port as expected in a host integer.
     322             :  */
     323           0 : int udp_base::get_port() const
     324             : {
     325           0 :     return f_port;
     326             : }
     327             : 
     328             : 
     329             : /** \brief Retrieve a copy of the address.
     330             :  *
     331             :  * This function returns a copy of the address as it was specified in the
     332             :  * constructor. This does not return a canonicalized version of the address.
     333             :  *
     334             :  * The address cannot be modified. If you need to send data on a different
     335             :  * address, create a new UDP client.
     336             :  *
     337             :  * \return A string with a copy of the constructor input address.
     338             :  */
     339           0 : std::string udp_base::get_addr() const
     340             : {
     341           0 :     return f_addr;
     342             : }
     343             : 
     344             : 
     345             : 
     346             : 
     347             : 
     348             : 
     349             : 
     350             : 
     351             : // ========================= CLIENT =========================
     352             : 
     353             : /** \brief Initialize a UDP client object.
     354             :  *
     355             :  * This function initializes the UDP client object using the address and the
     356             :  * port as specified.
     357             :  *
     358             :  * The port is expected to be a host side port number (i.e. 59200).
     359             :  *
     360             :  * The \p addr parameter is a textual address. It may be an IPv4 or IPv6
     361             :  * address and it can represent a host name or an address defined with
     362             :  * just numbers. If the address cannot be resolved then an error occurs
     363             :  * and constructor throws.
     364             :  *
     365             :  * \note
     366             :  * The socket is open in this process. If you fork() or exec() then the
     367             :  * socket will be closed by the operating system.
     368             :  *
     369             :  * \warning
     370             :  * We only make use of the first address found by getaddrinfo(). All
     371             :  * the other addresses are ignored.
     372             :  *
     373             :  * \exception udp_client_server_runtime_error
     374             :  * The server could not be initialized properly. Either the address cannot be
     375             :  * resolved, the port is incompatible or not available, or the socket could
     376             :  * not be created.
     377             :  *
     378             :  * \param[in] addr  The address to convert to a numeric IP.
     379             :  * \param[in] port  The port number.
     380             :  * \param[in] family  The family used to search for 'addr'.
     381             :  */
     382           0 : udp_client::udp_client(std::string const & addr, int port, int family)
     383           0 :     : udp_base(addr, port, family)
     384             : {
     385           0 : }
     386             : 
     387             : 
     388             : /** \brief Clean up the UDP client object.
     389             :  *
     390             :  * This function frees the address information structure and close the socket
     391             :  * before returning.
     392             :  */
     393           0 : udp_client::~udp_client()
     394             : {
     395           0 : }
     396             : 
     397             : 
     398             : /** \brief Send a message through this UDP client.
     399             :  *
     400             :  * This function sends \p msg through the UDP client socket. The function
     401             :  * cannot be used to change the destination as it was defined when creating
     402             :  * the udp_client object.
     403             :  *
     404             :  * The size must be small enough for the message to fit. In most cases we
     405             :  * use these in Snap! to send very small signals (i.e. 4 bytes commands.)
     406             :  * Any data we would want to share remains in the Cassandra database so
     407             :  * that way we can avoid losing it because of a UDP message.
     408             :  *
     409             :  * \param[in] msg  The message to send.
     410             :  * \param[in] size  The number of bytes representing this message.
     411             :  *
     412             :  * \return -1 if an error occurs, otherwise the number of bytes sent. errno
     413             :  * is set accordingly on error.
     414             :  */
     415           0 : int udp_client::send(char const * msg, size_t size)
     416             : {
     417           0 :     return static_cast<int>(sendto(f_socket.get(), msg, size, 0, f_addrinfo->ai_addr, f_addrinfo->ai_addrlen));
     418             : }
     419             : 
     420             : 
     421             : 
     422             : 
     423             : 
     424             : 
     425             : // ========================= SEVER =========================
     426             : 
     427             : /** \brief Initialize a UDP server object.
     428             :  *
     429             :  * This function initializes a UDP server object making it ready to
     430             :  * receive messages.
     431             :  *
     432             :  * The server address and port are specified in the constructor so
     433             :  * if you need to receive messages from several different addresses
     434             :  * and/or port, you'll have to create a server for each.
     435             :  *
     436             :  * The address is a string and it can represent an IPv4 or IPv6
     437             :  * address.
     438             :  *
     439             :  * Note that this function calls bind() to listen to the socket
     440             :  * at the specified address. To accept data on different UDP addresses
     441             :  * and ports, multiple UDP servers must be created.
     442             :  *
     443             :  * \note
     444             :  * The socket is open in this process. If you fork() or exec() then the
     445             :  * socket will be closed by the operating system.
     446             :  *
     447             :  * \warning
     448             :  * We only make use of the first address found by getaddrinfo(). All
     449             :  * the other addresses are ignored.
     450             :  *
     451             :  * \warning
     452             :  * Remember that the multicast feature under Linux is shared by all
     453             :  * processes running on that server. Any one process can listen for
     454             :  * any and all multicast messages from any other process. Our
     455             :  * implementation limits the multicast from a specific IP. However.
     456             :  * other processes can also receive you packets and there is nothing
     457             :  * you can do to prevent that.
     458             :  *
     459             :  * \exception udp_client_server_runtime_error
     460             :  * The udp_client_server_runtime_error exception is raised when the address
     461             :  * and port combinaison cannot be resolved or if the socket cannot be
     462             :  * opened.
     463             :  *
     464             :  * \param[in] addr  The address we receive on.
     465             :  * \param[in] port  The port we receive from.
     466             :  * \param[in] family  The family used to search for 'addr'.
     467             :  * \param[in] multicast_addr  A multicast address.
     468             :  */
     469           0 : udp_server::udp_server(std::string const & addr, int port, int family, std::string const * multicast_addr)
     470           0 :     : udp_base(addr, port, family)
     471             : {
     472             :     // bind to the very first address
     473             :     //
     474           0 :     int r(bind(f_socket.get(), f_addrinfo->ai_addr, f_addrinfo->ai_addrlen));
     475           0 :     if(r != 0)
     476             :     {
     477           0 :         int const e(errno);
     478             : 
     479             :         // reverse the address from the f_addrinfo so we know exactly
     480             :         // which one was picked
     481             :         //
     482             :         char addr_buf[256];
     483           0 :         switch(f_addrinfo->ai_family)
     484             :         {
     485           0 :         case AF_INET:
     486           0 :             inet_ntop(AF_INET
     487           0 :                     , &reinterpret_cast<struct sockaddr_in *>(f_addrinfo->ai_addr)->sin_addr
     488             :                     , addr_buf
     489             :                     , sizeof(addr_buf));
     490           0 :             break;
     491             : 
     492           0 :         case AF_INET6:
     493           0 :             inet_ntop(AF_INET6
     494           0 :                     , &reinterpret_cast<struct sockaddr_in6 *>(f_addrinfo->ai_addr)->sin6_addr
     495             :                     , addr_buf
     496             :                     , sizeof(addr_buf));
     497           0 :             break;
     498             : 
     499           0 :         default:
     500           0 :             strncpy(addr_buf, "Unknown Adress Family", sizeof(addr_buf));
     501           0 :             break;
     502             : 
     503             :         }
     504             : 
     505           0 :         SNAP_LOG_ERROR("the bind() function failed with errno: ")
     506           0 :                 (e)
     507           0 :                 (" (")
     508           0 :                 (strerror(e))
     509           0 :                 ("); address length ")
     510           0 :                 (f_addrinfo->ai_addrlen)
     511           0 :                 (" and address is \"")
     512           0 :                 (addr_buf)
     513           0 :                 ("\"");
     514           0 :         throw udp_client_server_runtime_error("could not bind UDP socket with: \"" + f_addr + ":" + std::to_string(port) + "\"");
     515             :     }
     516             : 
     517             :     // are we creating a server to listen to multicast packets?
     518             :     //
     519           0 :     if(multicast_addr != nullptr)
     520             :     {
     521             :         struct ip_mreqn mreq;
     522             : 
     523           0 :         std::stringstream decimal_port;
     524           0 :         decimal_port << f_port;
     525           0 :         std::string port_str(decimal_port.str());
     526             : 
     527           0 :         struct addrinfo hints = addrinfo();
     528           0 :         hints.ai_family = AF_UNSPEC;
     529           0 :         hints.ai_socktype = SOCK_DGRAM;
     530           0 :         hints.ai_protocol = IPPROTO_UDP;
     531             : 
     532             :         // we use the multicast address, but the same port as for
     533             :         // the other address
     534             :         //
     535           0 :         struct addrinfo * a(nullptr);
     536           0 :         r = getaddrinfo(multicast_addr->c_str(), port_str.c_str(), &hints, &a);
     537           0 :         if(r != 0 || a == nullptr)
     538             :         {
     539           0 :             throw udp_client_server_runtime_error("invalid address or port for UDP socket: \"" + addr + ":" + port_str + "\"");
     540             :         }
     541             : 
     542             :         // both addresses must have the right size
     543             :         //
     544           0 :         if(a->ai_addrlen != sizeof(mreq.imr_multiaddr)
     545           0 :         || f_addrinfo->ai_addrlen != sizeof(mreq.imr_address))
     546             :         {
     547           0 :             throw udp_client_server_runtime_error("invalid address type for UDP multicast: \"" + addr + ":" + port_str
     548           0 :                                                         + "\" or \"" + *multicast_addr + ":" + port_str + "\"");
     549             :         }
     550             : 
     551           0 :         memcpy(&mreq.imr_multiaddr, a->ai_addr->sa_data, sizeof(mreq.imr_multiaddr));
     552           0 :         memcpy(&mreq.imr_address, f_addrinfo->ai_addr->sa_data, sizeof(mreq.imr_address));
     553           0 :         mreq.imr_ifindex = 0;   // no specific interface
     554             : 
     555           0 :         r = setsockopt(f_socket.get(), IPPROTO_IP, IP_ADD_MEMBERSHIP, &mreq, sizeof(mreq));
     556           0 :         if(r < 0)
     557             :         {
     558           0 :             int const e(errno);
     559           0 :             throw udp_client_server_runtime_error("IP_ADD_MEMBERSHIP failed for: \"" + addr + ":" + port_str
     560           0 :                                                         + "\" or \"" + *multicast_addr + ":" + port_str + "\", "
     561           0 :                                                         + std::to_string(e) + strerror(e));
     562             :         }
     563             : 
     564             :         // setup the multicast to 0 so we don't receive other's
     565             :         // messages; apparently the default would be 1
     566             :         //
     567           0 :         int multicast_all(0);
     568           0 :         r = setsockopt(f_socket.get(), IPPROTO_IP, IP_MULTICAST_ALL, &multicast_all, sizeof(multicast_all));
     569           0 :         if(r < 0)
     570             :         {
     571             :             // things should still work if the IP_MULTICAST_ALL is not
     572             :             // set as we want it
     573             :             //
     574           0 :             int const e(errno);
     575           0 :             SNAP_LOG_WARNING("could not set IP_MULTICAST_ALL to zero, e = ")
     576           0 :                             (e)
     577           0 :                             (" -- ")
     578           0 :                             (strerror(e));
     579             :         }
     580             :     }
     581           0 : }
     582             : 
     583             : 
     584             : /** \brief Clean up the UDP server.
     585             :  *
     586             :  * This function frees the address info structures and close the socket.
     587             :  */
     588           0 : udp_server::~udp_server()
     589             : {
     590           0 : }
     591             : 
     592             : 
     593             : /** \brief Wait on a message.
     594             :  *
     595             :  * This function waits until a message is received on this UDP server.
     596             :  * There are no means to return from this function except by receiving
     597             :  * a message. Remember that UDP does not have a connect state so whether
     598             :  * another process quits does not change the status of this UDP server
     599             :  * and thus it continues to wait forever.
     600             :  *
     601             :  * Note that you may change the type of socket by making it non-blocking
     602             :  * (use the get_socket() to retrieve the socket identifier) in which
     603             :  * case this function will not block if no message is available. Instead
     604             :  * it returns immediately.
     605             :  *
     606             :  * \param[in] msg  The buffer where the message is saved.
     607             :  * \param[in] max_size  The maximum size the message (i.e. size of the \p msg buffer.)
     608             :  *
     609             :  * \return The number of bytes read or -1 if an error occurs.
     610             :  */
     611           0 : int udp_server::recv(char * msg, size_t max_size)
     612             : {
     613           0 :     return static_cast<int>(::recv(f_socket.get(), msg, max_size, 0));
     614             : }
     615             : 
     616             : 
     617             : /** \brief Wait for data to come in.
     618             :  *
     619             :  * This function waits for a given amount of time for data to come in. If
     620             :  * no data comes in after max_wait_ms, the function returns with -1 and
     621             :  * errno set to EAGAIN.
     622             :  *
     623             :  * The socket is expected to be a blocking socket (the default,) although
     624             :  * it is possible to setup the socket as non-blocking if necessary for
     625             :  * some other reason.
     626             :  *
     627             :  * This function blocks for a maximum amount of time as defined by
     628             :  * max_wait_ms. It may return sooner with an error or a message.
     629             :  *
     630             :  * \param[in] msg  The buffer where the message will be saved.
     631             :  * \param[in] max_size  The size of the \p msg buffer in bytes.
     632             :  * \param[in] max_wait_ms  The maximum number of milliseconds to wait for a message.
     633             :  *
     634             :  * \return -1 if an error occurs or the function timed out, the number of bytes received otherwise.
     635             :  */
     636           0 : int udp_server::timed_recv(char * msg, size_t const max_size, int const max_wait_ms)
     637             : {
     638             :     struct pollfd fd;
     639           0 :     fd.events = POLLIN | POLLPRI | POLLRDHUP;
     640           0 :     fd.fd = f_socket.get();
     641           0 :     int const retval(poll(&fd, 1, max_wait_ms));
     642             : 
     643             : //    fd_set s;
     644             : //    FD_ZERO(&s);
     645             : //#pragma GCC diagnostic push
     646             : //#pragma GCC diagnostic ignored "-Wold-style-cast"
     647             : //    FD_SET(f_socket.get(), &s);
     648             : //#pragma GCC diagnostic pop
     649             : //    struct timeval timeout;
     650             : //    timeout.tv_sec = max_wait_ms / 1000;
     651             : //    timeout.tv_usec = (max_wait_ms % 1000) * 1000;
     652             : //    int const retval(select(f_socket.get() + 1, &s, nullptr, &s, &timeout));
     653           0 :     if(retval == -1)
     654             :     {
     655             :         // poll() sets errno accordingly
     656           0 :         return -1;
     657             :     }
     658           0 :     if(retval > 0)
     659             :     {
     660             :         // our socket has data
     661           0 :         return static_cast<int>(::recv(f_socket.get(), msg, max_size, 0));
     662             :     }
     663             : 
     664             :     // our socket has no data
     665           0 :     errno = EAGAIN;
     666           0 :     return -1;
     667             : }
     668             : 
     669             : 
     670             : /** \brief Wait for data to come in, but return a std::string.
     671             :  *
     672             :  * This function waits for a given amount of time for data to come in. If
     673             :  * no data comes in after max_wait_ms, the function returns with -1 and
     674             :  * errno set to EAGAIN.
     675             :  *
     676             :  * The socket is expected to be a blocking socket (the default,) although
     677             :  * it is possible to setup the socket as non-blocking if necessary for
     678             :  * some other reason.
     679             :  *
     680             :  * This function blocks for a maximum amount of time as defined by
     681             :  * max_wait_ms. It may return sooner with an error or a message.
     682             :  *
     683             :  * \param[in] bufsize  The maximum size of the returned string in bytes.
     684             :  * \param[in] max_wait_ms  The maximum number of milliseconds to wait for a message.
     685             :  *
     686             :  * \return received string. nullptr if error.
     687             :  *
     688             :  * \sa timed_recv()
     689             :  */
     690           0 : std::string udp_server::timed_recv( int const bufsize, int const max_wait_ms )
     691             : {
     692           0 :     std::vector<char> buf;
     693           0 :     buf.resize( bufsize + 1, '\0' ); // +1 for ending \0
     694           0 :     int const r(timed_recv( &buf[0], bufsize, max_wait_ms ));
     695           0 :     if( r <= -1 )
     696             :     {
     697             :         // Timed out, so return empty string.
     698             :         // TBD: could std::string() smash errno?
     699             :         //
     700           0 :         return std::string();
     701             :     }
     702             : 
     703             :     // Resize the buffer, then convert to std string
     704             :     //
     705           0 :     buf.resize( r + 1, '\0' );
     706             : 
     707           0 :     std::string word;
     708           0 :     word.resize( r );
     709           0 :     std::copy( buf.begin(), buf.end(), word.begin() );
     710             : 
     711           0 :     return word;
     712             : }
     713             : 
     714             : 
     715             : 
     716             : 
     717           6 : } // namespace udp_client_server
     718             : 
     719             : // vim: ts=4 sw=4 et

Generated by: LCOV version 1.13