5

I've made my UDP server and client with boost::asio udp sockets. Everything looked good before I started sending more datagrams. They come correctly from client to server. But, they are united in my buffer into one message.

I use

udp::socket::async_receive with std::array<char, 1 << 18 > buffer

for making async request. And receive data through callback

void on_receive(const error_code& code, size_t bytes_transferred)

If I send data too often (every 10 milliseconds) I receive several datagrams simultaneously into my buffer with callback above. The question is - how to separate them? Note: my UDP datagrams have variable length. I don't want to use addition header with size, cause it'll make my code useless for third-party datagrams.

valery_l
  • 101
  • 3
  • Use a user-provided callback function in your `on_receive` message, that processes all the included messages in the single received packet. – Chad Jul 23 '12 at 14:45
  • Sure, it could be found here: [udp2tcp_tunnel](https://github.com/valery-l/udp2tcp_tunnel). It's in progress - yet a little ping-pong server (sending by TCP from client, receiving the same test message back from server by UDP). It seems to me I found the problem. If I send several buffers in **buffer sequence** by 'basic_datagram_socket::async_send_to(buffer_sequence, handler)', it'll come as **one** datagram (not **one** datagram for **each buffer**). – valery_l Jul 23 '12 at 14:57
  • Have you tried `buf_.clear()` after you receive each datagram? – Nikolai Fetissov Jul 23 '12 at 15:08
  • @Nikolai, no. But suppose, it's not a solution, cause I've got 'bytes_transferred' in my receive handler equal to the size of several datagrams together. I've solved the problem by sending only **one** buffer by 'basic_datagram_socket::async_send_to' at once, while still sending several buffers by 'async_write' for TCP socket. Maybe there is any better solution? – valery_l Jul 23 '12 at 15:29
  • That looks like the right solution. One UDP send dispatches one datagram (probably with gather I/O in case of multibuffer). The way to get around this on Linux is `sendmmsg` (http://man7.org/linux/man-pages/man2/sendmmsg.2.html), which I doubt is supported by asio. – Nikolai Fetissov Jul 23 '12 at 15:33
  • @Nikolai, thx for help. Didn't find anything like a `sendmmsg` for udp socket in asio. Unfortunately, my solution could lead to a problem, when the frequency of small messages will be too high. – valery_l Jul 23 '12 at 15:55
  • Two options here - a) drop asio for UDP case, resorting to native socket programming - might break your model; and b) pack more into one datagram, say everything you got in a single poll cycle - this means adding some small header(s) like count/length of application messages in a packet. – Nikolai Fetissov Jul 23 '12 at 16:02

1 Answers1

0

I believe this is a limitation in the way boost::asio handles stateless data streams. I noticed exactly the same behavior when using boost::asio for a serial interface. When I was sending packets with relatively large gaps between them I was receiving each one in a separate callback. As the packet size grew and the gap between the packets therefore decreased, it reached a stage when it would execute the callback only when the buffer was full, not after receipt of a single packet.

If you know exactly the size of the expected datagrams, then your solution of limiting the input buffer size is a perfectly sensible one, as you know a-priori exactly how large the buffer needs to be.

If your congestion is coming from having multiple different packet types being transmitted, so you can't pre-allocate the correct size buffer, then you could potentially create different sockets on different ports for each type of transaction. It's a little more "hacky" but given the virtually unlimited nature of ephemeral port availability, as long as you're not using 20,000 different packet types that would probably help you out as-well.

OcularProgrammer
  • 482
  • 2
  • 5
  • 19