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
|