Detect graceful close of boost blocking socket - c++

using read_until:
tcp::socket socket(...);
boost::asio::streambuf data;
boost::system::error_code read_until_error;
size_t bytes_found = boost::asio::read_until(
socket, data, "\n", read_until_error);
according to docs it doesn't throw and there is a lot of different error codes in boost::system::errc but which one is socket closed gracefully by peer? There is a conn reset but I think that's different.

Related

boost::asio::read with completion condition boost::asio::transfer_at_least(1) won't read until EOF

I have a Python echo server made in asyncio and a C++ client that makes use of Boost's Asio. While the echo server works properly, the client does not. The client sends a message that is 3000 characters long, but only receives a response that is 512 characters long from the server even though the client is set to listen until EOF.
Server:
import asyncio
async def handle_client(reader, writer):
received = (await reader.read(3000)).decode("utf8")
print(received)
response = received
writer.write(response.encode("utf8"))
await writer.drain()
writer.close()
async def run_server():
server = await asyncio.start_server(handle_client, "localhost", 15555)
async with server:
await server.serve_forever()
asyncio.run(run_server())
Client:
#include <boost/asio.hpp>
#include <string>
#include <iostream>
int main() {
boost::asio::io_context io_context;
boost::asio::ip::tcp::socket socket(io_context);
boost::asio::ip::tcp::resolver resolver(io_context);
socket.connect(boost::asio::ip::tcp::endpoint(boost::asio::ip::address::from_string("127.0.0.1"), 15555));
// This message is 3000 characters long.
std::string message = "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa";
boost::system::error_code error;
boost::asio::write(socket, boost::asio::buffer(message), error);
if (error) {
std::cerr << "error while sending the long message: " << error.message() << "\n";
}
boost::asio::streambuf receive_buffer;
boost::asio::read(socket, receive_buffer, boost::asio::transfer_at_least(1), error);
if (!error || error != boost::asio::error::eof) {
std::string received_data = boost::asio::buffer_cast<const char*>(receive_buffer.data());
std::cout << received_data << "\n";
}
}
The client output looks like this (according to Python, there is only 512 "a"s):
aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa²²²²
What seems to be wrong here? If using boost::asio::read with the completion condition boost::asio::transfer_at_least(1) is not the right way to read until EOF, how can I achieve this?
even though the client is set to listen until EOF.
How so? The code
boost::asio::streambuf receive_buffer;
boost::asio::read(socket, receive_buffer, boost::asio::transfer_at_least(1), error);
Specifically tells the read operation may return as soon as the completion condition is met: transfer_at_least(1). So, as soon as a single byte is read, the operation will complete.
Now, since packets on the wirte usually don't carry a single byte, you will get whatever was already in the TCP buffers or is the first packet to arrive.
Simply use boost::asio::transfer_all() instead.
It also looks like the condition is flawed. Did you mean
if (!error || error == boost::asio::error::eof) {
...
}

Flusing TCP after boost::asio::write

I have simple server written with boost:.asio
boost::asio::io_context io_context;
tcp::acceptor acceptor(io_context, tcp::endpoint(tcp::v4(), port));
tcp::socket socket(io_context);
acceptor.accept(socket);
boost::asio::ip::tcp::no_delay option(true);
socket.set_option(option);
for(int i = 0; i < 10; ++i) {
boost::system::error_code ignored_error;
boost::asio::write(socket, boost::asio::buffer("Hello, World!"), ignored_error);
}
My Problem is now that all 10 lines are send at once. But I want to have 10 separate messages. I thought boost::asio::ip::tcp::no_delay would help. But it does not.
The nodejs client I tested this looked:
import * as net from 'net';
const client = new net.Socket();
client.connect(3232, 'localhost', () => {
console.log('Connected');
});
client.on('data', data => {
console.log(`Received: ${data}`);
});
client.on('close', () => {
console.log('Connection closed');
});
client.on('data', ...) was only called once here.
Does anyone know how to "flush" after write?
Or is this a mistake in the nodejs client implementation?
TCP conceptually is a streaming protocol and has no concept of datagrams (unlike UDP). Once data is added to the TCP buffer, it will be sent in as few IP packets as possible. Also at the other end, all incoming packets boundaries info is lost. All incoming data is consolidated into a single buffer. So if you need framing, you need to implement it at the application level.

Applying a timeout to UDP call receive_from in ASIO

I have the following bit of ASIO code, that synchronously reads UDP packets. The problem is if no data packets of the given size have arrived in a given time frame (30 seconds) I'd like the recieve_from function to return with some kind of error to specif icy timeout.
for (;;)
{
boost::array<char, 1000> recv_buf;
udp::endpoint remote_endpoint;
asio::error_code error;
socket.receive_from(asio::buffer(recv_buf), // <-- require timeout
remote_endpoint, 0, error);
if (error && error != asio::error::message_size)
throw asio::system_error(error);
std::string message = make_daytime_string();
asio::error_code ignored_error;
socket.send_to(asio::buffer(message),
remote_endpoint, 0, ignored_error);
}
Looking at the documentation non of the UDP oriented calls support a time-out mechanism.
What is the correct way (also portable if possible) for having a time-out with syncronous UDP calls in ASIO?
As far as I know, this is not possible. By running a synchronous receive_from you've blocked code execution by a syscall recvmsg from #include <sys/socket.h>.
As portability goes, I cannot speak for Windows but a linux/bsd C-flavoured solution would look like this:
void SignalHandler(int signal) {
// do what you need to do, possibly informing about timeout and calling exit()
}
...
struct sigaction signal_action;
signal_action.sa_flags = 0;
sigemptyset(&signal_action.sa_mask);
signal_action.sa_handler = SignalHandler;
if (sigaction(SIGALRM, &signal_action, NULL) == -1) {
// handle error
}
...
int timeout_in_seconds = 5;
alarm(timeout_in_seconds);
...
socket.receive_from(asio::buffer(recv_buf), remote_endpoint, 0, error);
...
alarm(0);
If this is not at all feasible, I would recommend going full async and run it in a boost::asio::io_service.

Blocking sockets on Windows

I try to implement a simple http server with blocking sockets on Windows. Basically, I have a simple server that just write data to a socket when a network connection occurs before exit. The problem is that the last socket.send as no effect if I don't delay the process exit. Writing to this socket is supposed to block until all the data as been written.
I have tried to use the completion condition of write, to use the non_blocking method of the socket. I still get the same problem.
Note that the problem doesn't occur on Linux.
Here is the code:
#include <boost/asio.hpp>
int main(int argc, char *argv[]) {
char *address = "0.0.0.0";
char *port = "8180";
boost::asio::io_service io_service;
boost::asio::ip::tcp::acceptor acceptor(io_service);
boost::asio::ip::tcp::resolver resolver(io_service);
boost::asio::ip::tcp::resolver::query query(address, port);
boost::asio::ip::tcp::endpoint endpoint = *resolver.resolve(query);
acceptor.open(endpoint.protocol());
acceptor.bind(endpoint);
acceptor.listen();
boost::asio::ip::tcp::socket sock(io_service);
acceptor.accept(sock);
std::string body("Hello, World!");
sock.send(boost::asio::buffer(std::string("HTTP/1.1 200 OK\r\n")));
sock.send(boost::asio::buffer(std::string("Content-Length: ") + std::to_string(body.size()) + "\r\n\r\n"));
sock.send(boost::asio::buffer(body));
Sleep(1000); // The body would not be sent without this
return 0;
}
According to this post, on windows the send method will block only if the kernel runs out of socket buffers.
It also say that if the program is killed, the sockets are forcibly closed and the non sent data is discarded.
I wanted to add this as a comment but I don't have enough point, sorry about that.

boost tcp accept / connect takes too long

I have a server running very heavy 3D simulations that I want to display in real time on a client machine. For now I am running my tests in localhost to get rid of the network brandwidth and latency issues, and I use boost::asio to stream my data (geometry) through the network.
I have to use tcp because I have to compress my geometry, split it into multiple packages and then send it through the network, and on the client, gather the packages to rebuild my archive, so network packages have to arrive in the good order.
This works pretty well, I can run my simulation and stream my data at ~90-120fps, depending on the quantity of data to stream, which is very good.
My problem is that sometimes, it suddenly takes ~1second for the socket to connect() on the client, and consequently as much time for the server's to accept(). This causes my simulation to stop being streamed randomly, and I can't find the problem.
I though the problem could come from some kind of buffer overflow on the socket, preventing the server to write more data as long as the client didn't read some, but it can't be that, since I have no latency between the client and the server, so the client reads the packages fast enough (as soon as they arrive, basically)
Here's a shortened piece of code for the server:
while (1)
{
//archive some data in a stringstream using boost::archive...
boost::asio::io_service ioservice;
tcp::acceptor acceptor(ioservice, tcp::endpoint(tcp::v4(), PORT));
boost::system::error_code ignored_error;
tcp::socket socket(ioservice);
acceptor.accept(socket);
gettimeofday(&m_tv, NULL);
accept += (m_tv.tv_usec - m_timer);
m_timer = m_tv.tv_usec;
size_t bytes_sent = boost::asio::write(socket, boost::asio::buffer(ss.str()), boost::asio::transfer_all(), ignored_error);
}
and on the client I get something like:
while (1)
{
boost::asio::io_service io_service;
tcp::resolver resolver(io_service);
tcp::resolver::query query(IP, PORT);
tcp::resolver::iterator endpoint_iterator = resolver.resolve(query);
tcp::resolver::iterator end;
tcp::socket socket(io_service);
boost::system::error_code error = boost::asio::error::host_not_found;
while (error && endpoint_iterator != end)
{
socket.close();
socket.connect(*endpoint_iterator++, error);
}
if (error)
throw boost::system::system_error(error);
while(1)
{
boost::array<char, 200000> buf;
ss.write(buf.data(), bytes_received);
boost::system::error_code error;
bytes_received = socket.read_some(boost::asio::buffer(buf), error);
if (error == boost::asio::error::eof)
break;
else if (error)
throw boost::system::system_error(error);
}
}
I create a socket every frame, which is probably the problem, but I couldn't find an easier way of telling my client that he finished reading the package. By closing the socket every frame, I send eof to the client who then knows that he can build the archive using the data retrieved.
Is there something I can do to avoid opening a socket every frame, without having to check the content of my packages to know the size of the data to retrieve?