boost::asio udp - How do I get many mutable buffers filled? - c++

I'm trying to receive many udp messages from one async_receive call. My messages are approx. 60 bytes long.
I'm giving an async_receive call a buffer array very similar to the boost docs but can't seem to get all the buffers filled.
char d1[128];
char d2[128];
char d3[128];
boost::array<boost::asio::mutable_buffer, 3> bufs =
{
boost::asio::buffer(d1),
boost::asio::buffer(d2),
boost::asio::buffer(d3)
};
_socket.async_receive(bufs, handler);
When my handler gets called the bytes_transferred is equal to one message length (i.e. 60).
Any thoughts on how I can get the second and third buffer populated? Also, how do I now if the second and third mutable buffer were populated?

If you want to receive multiple datagrams in a single call, you generally (regardless of Boost) need to use recvmmsg. From what I can tell, Boost does not use recvmmsg, so you would need to use it yourself with the native socket held by Boost ASIO. The advantage of doing this is that you can reduce system calls when multiple datagrams are available.

Related

Does Asio's write_some really return immediately?

According to https://www.boost.org/doc/libs/1_73_0/doc/html/boost_asio/reference/basic_stream_socket/write_some/overload1.html,
The function call (write_some) will block until one or more bytes of the data has
been written successfully, or until an error occurs.
Here's the function:
template<
typename ConstBufferSequence>
std::size_t write_some(
const ConstBufferSequence & buffers);
as we see, a reference to the buffer is passed, which means that the implementation of write_some must consume the buffer immediately and entirely. It cannot borrow the buffer for itself to write (to a file descriptor) later.
However, the explanation in the page suggests that it does exactly that: once the first byte is written it can return and continue to write the remaining bytes. How is it possible? The reference to buffer may be destructed after the call.
basic_stream_socket::write_some is equivalent of Berkley socket send()( or write()) function.
Normally send() blocks until all bytes has been sent. But in rare cases, it can be interrupted by SIGNAL handler or timeout SO_SNDTIMEO at the moment when only part of the data has been transmitted. In this case, send returns the number of bytes sent (one or more bytes). And one should advance in the buffer and send the remaining bytes later.

Why is there no 64-bit API for winsock?

Calls such as send() and sendto() in the Winsock API take a primitive int to dictate the size of their buffer parameters. This obviously places a 32-bit limit on the maximum size buffer that may be sent.
Why is this? Is there a 64-bit Winsock2 API available that might use a more appropriate size type (e.g. size_t)?
On Linux similar calls use the size_t type for defining sizes.
There is no need for a function with larger size, actually the type could be short and still it won’t be a problem.
Sockets don’t send messages, they just transfer bytes. When you call send() the data may not get received in one chunk at the recv() call. You have to implement logic when receiving bytes to know if you got it all or not and call recv() again if not. So if you wanna send something larger than can be placed in an int? Just make multiple calls to send(). If your recv() code can’t handle that, it’s a bug, because it should.

C++ Boost asio get data size?

I am using the boost asio library to read some data using tcp. After using a.accept(*sock);, how to get the size of the 1st packet the client will send?
I use (sock->remote_endpoint().address()).to_string() to get the IP address of the user, so I guess there must be a similar simple way to get the size of the packet, right?
At the application level, it is often far more useful to know the number of bytes currently available for reading, rather than the packet size. The amount of data available for reading may be constructed from one or more TCP segments. In the OSI model, a TCP segment (Layer 4: Transport) may be constructed from one or more IP Layer packets (Layer 3: Network), and each packet may be constructed from one or more Ethernet frames (Layer 2: Data Link).
Therefore, I am going to assume the application is interested in knowing how many bytes to read, rather than knowing lower level details, such as the size of a packet. There are a few solutions to this problem:
Query the socket for how much data is available via socket::available(), then allocate the buffer accordingly.
std::vector<char> data(socket_.available());
boost::asio::read(socket_, boost::asio::buffer(data));
Use a class that Boost.Asio can grow in memory, such as boost::asio::streambuf. Some operations, such as boost::asio::read() accept streambuf objects as their buffer and will allocate memory as is required for the operation. However, a completion condition should be provided; otherwise, the operation will continue until the buffer is full.
boost::asio::streambuf data;
boost::asio::read(socket_, data,
boost::asio::transfer_at_least(socket_.available()));
As Igor R. suggests in the comments, incorporate length as part of the communication protocol. Check the Boost.Asio examples for examples of communication protocols. Focus on the protocol, not necessarily on the Boost.Asio API.
In a fixed length protocol, a constant byte size is used to indicate message boundaries, such as in the Boost.Asio Porthopper example. As the reader knows the size of the message, the reader can allocate a buffer in advance.
In a variable length protocol, such as the one used in the Boost.Asio Chat example, a message is often divided into two parts: a header and a body. One approach is to have a a fixed size header that contains various meta-information, such as the length of the body. This allows an application to read a header into a fixed size buffer, extract the body length, allocate a buffer for the body, then read the body.
// Read fixed header.
std::vector<char> data(fixed_header_size);
boost::asio::read(socket_, boost::asio::buffer(data));
protocol::header header(data);
network_to_local(header); // Handle endianess.
// Read body.
data.resize(header.body_length());
boost::asio::read(socket_, boost::asio::buffer(data));
protocol::body body(data);
network_to_local(body); // Handle endianess.
On the other hand, if I am mistaken, and you do need the total length of a packet, then one can use the basic_raw_socket. Boost.Asio's ICMP example demonstrates reading IPv4 packets from a socket, and extracting the header's field values.

boost::asio packet order and contiguity

I am using boost::asio::ip::tcp::socketto send and receive data in a client/server application. I use simple selfmade data packets consisting of a header containing the size of the packet plus some flags and the "real" data.
I send my packets using boost::asio::write. In some cases I have a lot of packets to send to one client. Naive the fastest option would be to send them all at once. E.g.
async_write(socket, buffer(p[0].str(),p[0].size()), &callback);
async_write(socket, buffer(p[1].str(),p[1].size()), &callback);
async_write(socket, buffer(p[2].str(),p[2].size()), &callback);
But if I do it like this, is there a guarantee that my packets arrive as a whole at the client? So in the example if the first 4 bytes of the packet represent the size of the remaining packet, can I call four times
uint32 size;
read(socket, buffer(&size,4));
char data[size];
read(socket, buffer(data,size));
and be sure that each data contains one packet (appart from the size uint32)? Or would it work if I send the next packet in the callback function?
If this infact works one other question is about the ordering. Can I be sure that the order, the packets arrive at the client, is the same as the one I send them from the server?
First and foremost, TCP sockets operate on streams of data, not packets. Secondly the async_write() documentation explicitly states a single outstanding operation per socket can be in-flight:
This operation is implemented in terms of zero or more calls to the
stream's async_write_some function, and is known as a composed
operation. The program must ensure that the stream performs no other
write operations (such as async_write, the stream's async_write_some
function, or any other composed operations that perform writes) until
this operation completes.
You will need to chain your operations: invoke a second async_write() only after the completion handler for the first operation has been invoked.

About recv and the read buffer - C Berkeley Sockets

I am using berkeley sockets and TCP (SOCK_STREAM sockets).
The process is:
I connect to a remote address.
I send a message to it.
I receive a message from it.
Imagine I am using the following buffer:
char recv_buffer[3000];
recv(socket, recv_buffer, 3000, 0);
Questions are:
How can I know if after calling recv first time the read buffer is empty or not? If it's not empty I would have to call recv again, but if I do that when it's empty I would have it blocking for much time.
How can I know how many bytes I have readed into recv_buffer? I can't use strlen because the message I receive can contain null bytes.
Thanks.
How can I know if after calling recv
first time the read buffer is empty or
not? If it's not empty I would have to
call recv again, but if I do that when
it's empty I would have it blocking
for much time.
You can use the select or poll system calls along with your socket descriptor to tell if there is data waiting to be read from the socket.
However, usually there should be an agreed-upon protocol that both sender and receiver follow, so that both parties know how much data is to be transferred. For example, perhaps the sender first sends a 2-byte integer indicating the number of bytes it will send. The receiver then first reads this 2-byte integer, so that it knows how many more bytes to read from the socket.
Regardless, as Tony pointed out below, a robust application should use a combination of length-information in the header, combined with polling the socket for additional data before each call to recv, (or using a non-blocking socket). This will prevent your application from blocking in the event that, for example, you know (from the header) that there should still be 100 bytes remaining to read, but the peer fails to send the data for whatever reason (perhaps the peer computer was unexpectedly shut off), thus causing your recv call to block.
How can I know how many bytes I have
readed into recv_buffer? I can't use
strlen because the message I receive
can contain null bytes.
The recv system call will return the number of bytes read, or -1 if an error occurred.
From the man page for recv(2):
[recv] returns the number of bytes
received, or -1 if an error occurred.
The return value will be 0 when the
peer has performed an orderly
shutdown.
How can I know if after calling recv first time the read buffer is empty or not?
Even the first time (after accepting a client), the recv can block and fail if the client connection has been lost. You must either:
use select or poll (BSD sockets) or some OS-specific equivalent, which can tell you whether there is data available on specific socket descriptors (as well as exception conditions, and buffer space you can write more output to)
you can set the socket to be nonblocking, such that recv will only return whatever is immediately available (possibly nothing)
you can create a thread that you can afford to have block recv-ing data, knowing other threads will be doing the other work you're concerned to continue with
How can I know how many bytes I have readed into recv_buffer? I can't use strlen because the message I receive can contain null bytes.
recv() returns the number of bytes read, or -1 on error.
Note that TCP is a byte stream protocol, which means that you're only guaranteed to be able to read and write bytes from it in the correct order, but the message boundaries are not guaranteed to be preserved. So, even if the sender has made a large single write to their socket, it can be fragmented en route and arrive in several smaller blocks, or several smaller send()/write()s can be consolidated and retrieved by one recv()/read().
For that reason, make sure you loop calling recv until you either get all the data you need (i.e. a complete logical message you can process) or an error. You should be prepared/able to handle getting part/all of subsequent sends from your client (if you don't have a protocol where each side only sends after getting a complete message from the other, and are not using headers with message lengths). Note that doing recvs for the message header (with length) then the body can result in a lot more calls to recv(), with a potential adverse affect on performance.
These reliability issues are often ignored. They manifest less often when on a single host, a reliable and fast LAN, with less routers and switches involved, and fewer or non-concurrent messages. Then they may break under load and over more complex networks.
If the recv() returns fewer than 3000 bytes, then you can assume that the read buffer was empty. If it returns 3000 bytes in your 3000 byte buffer, then you'd better know whether to continue. Most protocols include some variation on TLV - type, length, value. Each message contains an indicator of the type of message, some length (possibly implied by the type if the length is fixed), and the value. If, on reading through the data you did receive, you find that the last unit is incomplete, you can assume there is more to be read. You can also make the socket into a non-blocking socket; then the recv() will fail with EAGAIN or EWOULDBLOCK if there is no data read for reading.
The recv() function returns the number of bytes read.
ioctl() with the FIONREAD option tells you how much data can currently be read without blocking.