Socket set option explanation - c++

I have small piece of code
boost::asio::ip::tcp::no_delay option(true);
boost::asio::ip::tcp::socket* sock = new boost::asio::ip::tcp::socket(ios);
sock->set_option(option);
_session_acceptor.async_accept(*sock,
boost::bind(&server::playerAccept, this, sock, boost::asio::placeholders::error));
If i call set_option on socket before accepting server dont accept any connections. But if i call set_option after connections are accepted. Is there any magic?

You should call set_option on acceptor, not socket. Example from my project:
Listener::Listener(int port)
: acceptor(io, ip::tcp::endpoint(ip::tcp::v4(), port))
, socket(io) {
boost::asio::ip::tcp::no_delay opt_nodelay(true);
acceptor.set_option(opt_nodelay);
start_accept();

Related

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.

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.

UDP stream reading like socat

I'm developing a tool which is analyzing video stream.
I've worked with a file, I generate the file with this socat command (that someone's given to me):
socat -u UDP4-RECV:1234,ip-add-membership=xxx.xxx.xxx.xxx:0.0.0.0 CREATE:temp.ts
But now I'd like to work directly with the UDP stream.
With this code, I've tried to read the first received block and write it on the console, but I don#t get anything - the program gets stuck...
void Decoder::open_udp_stream(std::string ip_adress)
{
boost::asio::io_service io_service;
udp::endpoint receiver_endpoint (boost::asio::ip::address::from_string("xxx.xxx.xxx.xxx"), 1234);
udp::socket socket(io_service);
socket.open(udp::v4());
boost::array<char, 128> recv_buf;
udp::endpoint sender_endpoint;
size_t len = socket.receive_from(boost::asio::buffer(recv_buf), sender_endpoint);
std::cout.write(recv_buf.data(), len);
}
What should I do to get the blocks from this IP, as with the socat command ?
This code is working (thanks David Schwarz)
void Decoder::open_udp_stream(std::string ip_adress)
{
boost::asio::io_service io_service;
boost::asio::ip::udp::socket socket_(io_service);
boost::asio::ip::udp::endpoint sender_endpoint;
// Create the socket so that multiple may be bound to the same address.
boost::asio::ip::udp::endpoint listen_endpoint(
boost::asio::ip::address::from_string("0.0.0.0"), 1234);
socket_.open(listen_endpoint.protocol());
socket_.set_option(boost::asio::ip::udp::socket::reuse_address(true));
socket_.bind(listen_endpoint);
// Join the multicast group.
socket_.set_option(
boost::asio::ip::multicast::join_group(boost::asio::ip::address::from_string("xxx.xxx.xxx.xxx")));
boost::array<char, BUF_SIZE> recv_buf;
size_t len = socket_.receive_from(boost::asio::buffer(recv_buf), sender_endpoint);
std::cout.write(recv_buf.data(), len);
}

How do I correctly randomly assign a port to a test HTTP server using boost asio?

I am using boost asio to create a test server to test an http client. This will run on the same machine as the client. Now what I want to do is create a server with a randomly assigned port. I have looked at this thread here: Using boost::asio is there a portable way to find out free port number but I'm frankly still a little baffled.
My code looks something like this:
boost::asio::io_service service;
tcp::acceptor acceptor(service);
unsigned short port(0);
tcp::endpoint endPoint(tcp::endpoint(tcp::v4(), port);
acceptor.open(endPoint.protocol());
acceptor.set_option(tcp::acceptor::reuse_address(true));
acceptor.bind(endPoint);
port = endPoint.port();
std::cout<<port<<std::endl; // prints 0
My thoughts were that by creating an endpoint with 'port 0' and then trying to bind to that port, it should cause an available port to be randomly assigned but this doesn't seem to be the case. Any idea what I'm doing wrong?
Cheers.
boost::asio::io_service service;
boost::asio::ip::tcp::acceptor acceptor(service);
unsigned short port(0);
boost::asio::ip::tcp::endpoint endPoint(tcp::endpoint(tcp::v4(), port);
acceptor.open(endPoint.protocol());
acceptor.set_option(tcp::acceptor::reuse_address(true));
acceptor.bind(endPoint);
m_acceptor.listen();
boost::asio::ip::tcp::endpoint le = acceptor.local_endpoint(); //THIS LINE SOLVES IT
port = le.port();
// port = endPoint.port(); // This is didn't work for me
Helpful answer
Similar question
You can shorten this by using a different constructor for the acceptor:
using boost::asio;
io_service service;
ip::tcp::acceptor acceptor(service, ip::tcp::endpoint(ip::tcp::v4(), 0));
unsigned short port = acceptor.local_endpoint().port();
This constructor calls open(), bind() and listen() on the acceptor.
Fixed. I needed to do:
boost::asio::io_service service;
tcp::acceptor acceptor(service);
unsigned short port(0);
tcp::endpoint endPoint(tcp::endpoint(tcp::v4(), port);
acceptor.open(endPoint.protocol());
acceptor.set_option(tcp::acceptor::reuse_address(true));
acceptor.bind(endPoint);
m_acceptor.listen(); // NEEDED TO ADD THIS BIT!
port = endPoint.port();
std::cout<<port<<std::endl; // prints 0

Unable to reconnect a boost asio socket client

I am using boost::asio::io_service to manage some asynchronous TCP communication. The asio version is boost::asio 1.46. I want to make the client reconnect to the server when the server goes down.
Code follows:
tot_client::tot_client(boost::asio::io_service& io_service,
tcp::resolver::iterator endpoint_iterator)
: io_service_(io_service),
socket_(io_service)
{
boost::shared_ptr<tcp::socket> ptr_temp(new tcp::socket(io_service));
socket_ptr =ptr_temp;
socket_ptr->async_connect(tcp::endpoint(boost::asio::ip::address_v4::loopback(),2012),
boost::bind(&tot_client::handle_connect, this,
boost::asio::placeholders::error));
}
if the server is down, my client checks if the socket is open. If the socket isn't open, it then tries to reconnect to the server:
if(socket_ptr.use_count()&&socket_ptr->is_open())
{
//...
} else
{
reconnect ();
}
The reconnect code is here:
void tot_client::reconnect()
{
try
{
std::cout<<" socket_ptr.reset(new tcp::socket(io_service_) ); "<<endl;
socket_ptr.reset(new tcp::socket(io_service_) );
//socket_ptr->connect(tcp::endpoint(boost::asio::ip::address_v4::loopback(),2012));
socket_ptr->async_connect(tcp::endpoint(boost::asio::ip::address_v4::loopback(),2012),
boost::bind(&tot_client::handle_connect, this,
boost::asio::placeholders::error));
}
catch (std::exception& e )
{
std::cerr<<e.what()<<endl;
}
}
The socket async_connect doesn't work! If I directly use the connect method, the server can receive the socket, but the io_service in the client doesn't work anyway.
Can someone tell me the right way to reconnect to the server? Thanks a lot!
Are you sure io_service is still running?
if io_service stopped working after it ran out of work, you need to call
io_service.reset();
io_service.run();