Flusing TCP after boost::asio::write - c++

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.

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) {
...
}

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?

Issue with broadcast using Boost.Asio

I apologize in advance if the question has been previously answered, but I've searched and found nothing that helps me. As indicated by the question's title, I'm trying to broadcast a package from a server to a set of clients listening for any message.
The client will count the number of messages it receives during one second.
The server side of things goes like this:
class Server
{
public:
Server(boost::asio::io_service& io)
: socket(io, udp::endpoint(udp::v4(), 8888))
, broadcastEndpoint(address_v4::broadcast(), 8888)
, tickHandler(boost::bind(&Server::Tick, this, boost::asio::placeholders::error))
, timer(io, boost::posix_time::milliseconds(20))
{
socket.set_option(boost::asio::socket_base::reuse_address(true));
socket.set_option(boost::asio::socket_base::broadcast(true));
timer.async_wait(tickHandler);
}
private:
void Tick(const boost::system::error_code&)
{
socket.send_to(boost::asio::buffer(buffer), broadcastEndpoint);
timer.expires_at(timer.expires_at() + boost::posix_time::milliseconds(20));
timer.async_wait(tickHandler);
}
private:
udp::socket socket;
udp::endpoint broadcastEndpoint;
boost::function<void(const boost::system::error_code&)> tickHandler;
boost::asio::deadline_timer timer;
boost::array<char, 100> buffer;
};
It is initialized and run in the following way:
int main()
{
try
{
boost::asio::io_service io;
Server server(io);
io.run();
}
catch (const std::exception& e)
{
std::cerr << e.what() << "\n";
}
return 0;
}
This (apparently) works fine. Now comes the client...
void HandleReceive(const boost::system::error_code&, std::size_t bytes)
{
std::cout << "Got " << bytes << " bytes\n";
}
int main(int argc, char* argv[])
{
if (argc != 2)
{
std::cerr << "Usage: " << argv[0] << " <host>\n";
return 1;
}
try
{
boost::asio::io_service io;
udp::resolver resolver(io);
udp::resolver::query query(udp::v4(), argv[1], "1666");
udp::endpoint serverEndpoint = *resolver.resolve(query);
//std::cout << serverEndpoint.address() << "\n";
udp::socket socket(io);
socket.open(udp::v4());
socket.bind(serverEndpoint);
udp::endpoint senderEndpoint;
boost::array<char, 300> buffer;
auto counter = 0;
auto start = std::chrono::system_clock::now();
while (true)
{
socket.receive_from(boost::asio::buffer(buffer), senderEndpoint);
++counter;
auto current = std::chrono::system_clock::now();
if (current - start >= std::chrono::seconds(1))
{
std::cout << counter << "\n";
counter = 0;
start = current;
}
}
}
catch (const std::exception& e)
{
std::cerr << e.what() << "\n";
}
This works when running both the server and client on the same machine, but doesn't when I run the server on a machine different from that of where I run the client.
First thing is, it seems odd to me that I have to resolve the server's address. Perhaps I don't know how broadcasting really works, but I thought the server would send a message using its socket with the broadcast option turned on, and it would arrive to all the sockets in the same network.
I read you should bind the client's socket to the address_v4::any() address. I did, it doesn't work (says something about a socket already using the address/port).
Thanks in advance.
PS: I'm under Windows 8.
I am a bit surprised this works on the same machine. I would not have expected the client, listening to port 1666, to receive data being sent to the broadcast address on port 8888.
bind() assigns a local endpoint (composed of a local address and port) to the socket. When a socket binds to an endpoint, it specifies that the socket will only receive data sent to the bound address and port. It is often advised to bind to address_v4::any(), as this will use all available interfaces for listening. In the case of a system with multiple interfaces (possible multiple NIC cards), binding to a specific interface address will result in the socket only listening to data received from the specified interface[1]. Thus, one might find themselves obtaining an address through resolve() when the application wants to bind to a specific network interface and wants to support resolving it by providing the IP directly (127.0.0.1) or a name (localhost).
It is important to note that when binding to a socket, the endpoint is composed of both an address and port. This is the source of my surprise that it works on the same machine. If the server is writing to broadcast:8888, a socket bound to port 1666 should not receive the datagram. Nevertheless, here is a visual of the endpoints and networking:
.--------.
.--------.|
.--------. address: any address: any .--------.||
| | port: any / \ port: 8888 | |||
| server |-( ----------->| address: broadcast |----------> )-| client ||'
| | \ port: 8888 / | |'
'--------' '--------'
The server binds to any address and any port, enables the broadcast option, and sends data to the remote endpoint (broadcast:8888). Clients bound to the any address on port 8888 should receive the data.
A simple example is as follows.
The server:
#include <boost/asio.hpp>
int main()
{
namespace ip = boost::asio::ip;
boost::asio::io_service io_service;
// Server binds to any address and any port.
ip::udp::socket socket(io_service,
ip::udp::endpoint(ip::udp::v4(), 0));
socket.set_option(boost::asio::socket_base::broadcast(true));
// Broadcast will go to port 8888.
ip::udp::endpoint broadcast_endpoint(ip::address_v4::broadcast(), 8888);
// Broadcast data.
boost::array<char, 4> buffer;
socket.send_to(boost::asio::buffer(buffer), broadcast_endpoint);
}
The client:
#include <iostream>
#include <boost/asio.hpp>
int main()
{
namespace ip = boost::asio::ip;
boost::asio::io_service io_service;
// Client binds to any address on port 8888 (the same port on which
// broadcast data is sent from server).
ip::udp::socket socket(io_service,
ip::udp::endpoint(ip::udp::v4(), 8888 ));
ip::udp::endpoint sender_endpoint;
// Receive data.
boost::array<char, 4> buffer;
std::size_t bytes_transferred =
socket.receive_from(boost::asio::buffer(buffer), sender_endpoint);
std::cout << "got " << bytes_transferred << " bytes." << std::endl;
}
When the client is not co-located with the server, then it could be a variety of network related issues:
Verify connectivity between the server and client.
Verify firewall exceptions.
Verify broadcast support/exceptions on the routing device.
Use a network analyzer tool, such as Wireshark, to verify that the time to live field in the packets is high enough that it will not be discarded during routing.
1. On Linux, broadcast datagrams received by an adapter will not be passed to a socket bound to a specific interface, as the datagram's destination is set to the broadcast address. On the other hand, Windows will pass broadcast datagrams received by an adapter to sockets bound to a specific interface.

boost::asio checker

try
{
boost::asio::io_service io_service;
tcp::acceptor acceptor(io_service, tcp::endpoint(tcp::v4(), 13));
for (;;)
{
tcp::socket socket(io_service);
acceptor.accept(socket);
//how do i make a checker here if the client is not sending anything then server send or if the client sending then server recive
}
}
catch (std::exception& e)
{
std::cerr << e.what() << std::endl;
}
how do i make a checker, if the client is not sending anything then server send or if the client sending then server recive
The question is not immediately clear.
I would start an async_read() with an associated deadline_timer set to an appropriate value. If your timer expires before any reading was performed, then have your server send its data.