boost asio broadcast not going out on all interfaces - c++

If set up a program with boost asio.
Broadcasts are working fine, if only one network interface is present.
However, if there are more network interfaces each broadcast is being sent on one interface only. The interface changes randomly. As observed by wireshark.
I'd expect each broadcast to go out on every interface.
Who's wrong? Me, boost or my understanding of how to use boost. Well, I'm aware, that the latter is the most probable :).
And how can I get the expected behavior.
int myPort=5000;
boost::asio::io_context io_Context{};
boost::asio::ip::udp::socket socket{io_Context};
std::thread sendWorkerThread;
void SendWorkerStart() {
boost::asio::executor_work_guard<decltype(io_Context.get_executor())> work { io_Context.get_executor() };
io_Context.run();
}
void setupSocket() {
socket.set_option(boost::asio::socket_base::reuse_address(true));
socket.set_option(boost::asio::socket_base::broadcast(true));
boost::system::error_code ec;
socket.bind(boost::asio::ip::udp::endpoint(boost::asio::ip::address_v4::any(), myPort), ec);
sendWorkerThread = std::thread(udpSocket_c::SendWorkerStart, this);
SendWorkerStart();
}
void SendBroadcast(UdpMessage_t &&message, int size) {
boost::system::error_code ec;
std::lock_guard<std::mutex> lockGuard(sendMutex);
udp::endpoint senderEndpoint(boost::asio::ip::address_v4::broadcast(), myPort);
socket.async_send_to(boost::asio::buffer(message->data(), size), senderEndpoint,
[this](const boost::system::error_code& error,
std::size_t bytes_transferred) { /* nothing to do */} );
}
Thanks for your help.
Edit: It's now running on Windows, but needs to work also in Linux.

As suggested by Alan Birtles in the comments to the question i found an explanation here:
UDP-Broadcast on all interfaces
I solved the issue by iterating over he configured interfaces and sending the broadcast to each networks broadcast address as suggested by the linked answer.

Related

Simple Boost::Asio asynchronous UDP echo server

I'm currently making my way through a book on C++ called "C++ Crash Course". The chapter on networking shows how to use Boost::Asio to write a simple uppercasing TCP server (synchronously or asynchronously). One of the excersises is to recreate it with UDP, which is what I'm having trouble with. Here's my implementation:
#include <iostream>
#include <boost/asio.hpp>
#include <boost/algorithm/string/case_conv.hpp>
using namespace boost::asio;
struct UdpServer {
explicit UdpServer(ip::udp::socket socket)
: socket_(std::move(socket)) {
read();
}
private:
void read() {
socket_.async_receive_from(dynamic_buffer(message_),
remote_endpoint_,
[this](boost::system::error_code ec, std::size_t length) {
if (ec || this->message_ == "\n") return;
boost::algorithm::to_upper(message_);
this->write();
}
);
}
void write() {
socket_.async_send_to(buffer(message_),
remote_endpoint_,
[this](boost::system::error_code ec, std::size_t length) {
if (ec) return;
this->message_.clear();
this->read();
}
);
}
ip::udp::socket socket_;
ip::udp::endpoint remote_endpoint_;
std::string message_;
};
int main() {
try {
io_context io_context;
ip::udp::socket socket(io_context, ip::udp::v4(), 1895);
UdpServer server(std::move(socket));
io_context.run();
} catch (std::exception & e) {
std::cerr << e.what() << std::endl;
}
}
(Note: The original example uses enable_shared_from_this to capture this by shared_ptr into the lambdas, but I deliberately omitted it to see what would happen without it.)
My code does not compile, and I feel it will take me a thousand years to fully parse the error message (posted on pastebin.com since it's enormous).
It seems the issue is that the buffers are being used/constructed the wrong way, but I have no idea what exactly is wrong with this code. The few answers here on SO concerning Asio either use TCP or tackle an entirely different problem, so the mistake I made has to be really basic. I didn't find anything relevant in the Asio docs.
To be fair, Asio seems way too complicated to my newbie self. Maybe I just don't have the qualifications to use it right now. Nonetheless, I would still like to get the exercise done and move on. Any help would be appreciated.
Templates have the ugliest of compiler error messages. You often just have to go through the compiler error output and look for the first reference in your own source file. Ala:
/home/atmaks/Code/CCC_chapter20/main.cpp:53:9: required from here
In any case, on Visual Studio, the error was a bit more clear. (Not really, it just identified the offending line better).
Stare at it and contemplate all your life's decisions that led you to want to be developing in C++ in the first place. :)
I can't for the life of me figure out how to get dynamic_buffer to work. It may simply be the case that async_read doesn't like this type. And I think that actually makes sense for UDP. The receive buffer has to be sized before the recvfrom call in a synchronous mode. And I suspect async UDP, especially for Windows, the buffer has to be passed down to the kernel to be filled up. By then it's too late to be sized.
Asio lacks proper documentation and leaves us with cryptic template types to figure out. And the only Asio documentation that is worthwhile are the decent examples - none of which reference dynamic_buffer.
So let's change to a fixed sized buffer for receiving.
While we're at it, it didn't like your socket constructor and threw an exception. So I fixed it up such that it will work.
#include <iostream>
#include <boost/asio.hpp>
#include <boost/algorithm/string/case_conv.hpp>
using namespace boost::asio;
struct UdpServer {
explicit UdpServer(ip::udp::socket socket)
: socket_(std::move(socket)) {
read();
}
private:
void read() {
socket_.async_receive_from(buffer(data_, 1500),
remote_endpoint_,
[this](boost::system::error_code ec, std::size_t length) {
if (ec)
{
return;
}
data_[length] = '\0';
if (strcmp(data_, "\n") == 0)
{
return;
}
boost::algorithm::to_upper(data_);
this->write();
}
);
}
void write() {
socket_.async_send_to(buffer(data_, strlen(data_)),
remote_endpoint_,
[this](boost::system::error_code ec, std::size_t length) {
if (ec) return;
data_[0] = '\0';
this->read();
}
);
}
ip::udp::socket socket_;
ip::udp::endpoint remote_endpoint_;
char data_[1500 + 1]; // +1 for we can always null terminate safely
};
int main() {
try {
io_context io_context;
ip::udp::endpoint ep(ip::udp::v6(), 1895); // also listens on ipv4
ip::udp::socket sock(io_context, ep);
UdpServer server(std::move(sock));
io_context.run();
}
catch (std::exception& e) {
std::cerr << e.what() << std::endl;
}
}
Update
I did get dynamic_buffer to work, but it still requires a pre-allocation to be made.
Update the the start of the read() function as follows:
void read() {
auto db = dynamic_buffer(message_);
auto b = db.prepare(1500);
socket_.async_receive_from(b,
...
That at least lets you stick with std::string instead of using a flat C array.
And now for evidence that it's working:

How to make a multi-client server with synchronous dataread/write functions?

Okay, so I might have got myself a big problem here. All this time, I've been basing my code in something I might not have wanted, that is, I'm using synchronous boost::asio functions with a server that can have multiple clients at the same time. Here it is:
void session(tcp::socket socket, std::vector<Player>* pl)
{
debug("New connection! Reading username...\n");
/* ...Username verification code removed... */
debug("Client logged in safely as ");
debug(u->name);
debug("\n");
for (;;)
{
boost::array<unsigned char, 128> buf;
size_t len = socket.read_some(boost::asio::buffer(buf), error);
if (error == boost::asio::error::eof)
{
debug("Connection ended.\n");
break; // Connection closed cleanly by peer.
}
else if (error)
throw boost::system::system_error(error); // Some other error.
DataHeader ins = static_cast<DataHeader>(buf.data()[0]);
std::vector<unsigned char> response;
/* ... Get appropiate response... */
// send response
boost::system::error_code ignored_error;
boost::asio::write(socket, boost::asio::buffer(response), ignored_error);
//debug("Sent ");
//debug(response.size());
//debug("B to client.\n");
}
}
As you can see from the code, I'm using read_some and write functions in a non-ideal scenario. Now, the question is, how did I make this code usable for multiple clients at the same time? Well, I used threads:
int main()
{
try
{
boost::asio::io_context io_context;
tcp::acceptor acceptor(io_context, tcp::endpoint(tcp::v4(), 13));
debug("Ready.\n");
for (;;)
{
std::thread(session, acceptor.accept(), &players).detach(); // Accept incoming clients
}
}
catch (std::exception& e)
{
std::cerr << e.what() << std::endl;
}
return 0;
}
Now, I've never had a problem with this setup until recently, that I started testing multiple clients at the same time on one server. This made the server crash many times, and just until now, I thought the problem were just connection issues. However, now I've started to wonder, "Might the problem be the synchronous functions?"
All the examples I've seen until now of multi-client servers use async functions, and maybe it's because they are needed. So, my final question is, do I really need async functions? Is there anything wrong with this code to make it crash? And finally, if async functions are needed, how could I implement them? Many thanks in advance!
As user VTT has pointed out, although this approach may work for a little bit, it's just better to switch to async functions due to resource exhaustion, so, I'll just redo the entire server to implement them.

boost::asio server with simple functions

Guys I really need your help. I'm learning boost::asio and I have 2 problems that I can't deal for days...
Here is an example of a simple echo server done by myself:
int main(
{
// crate a server, binding it and listening connections
// acceptor server;
//socket client
server.async_accept(client, boost::bind(accept_handle, _1, &server, &client));
io_service.run();
return 0;
}
void accept_handle(const boost::system::error_code& eCode, boost::asio::ip::tcp::acceptor* server, boost::asio::ip::tcp::socket* client)
{
char data[43];
client->async_read_some(boost::asio::buffer(data, 20), boost::bind(read_handle, _1, _2, server, client));
}
void read_handle(const boost::system::error_code& eCode, size_t bytes)
{
char data_buf[20] = "hello";
client->async_write_some(boost::buufer(data, 5), boost::bind(write_handle, _1, _2, server, client));
}
void write_accept(const boost::system::error_code& eCode, size_t bytes)
{
boost::asio::ip::tcp::socket newConnection(server->get_ioservice)); // taking he io_service of the server
server->async_accept(newConnection, boost::bind(accept_handle, _1, server, client));
}
The problem is the server accept one client and it does not accept other pending client.. where am i doing wrong here
NOTE: I wrote this code in notepad so sorry for syntax errors if there are any.
Thanks for your help in advance!!!
The code can only accept one connection because it is not calling async_accept in the accept_handle function.
The code may also have a problem with object lifetimes: it would be wise to use shared pointers to manage the clients see: Boost async_* functions and shared_ptr's.

Server and Client at same time with Boost-Asio

I am an AspNet programmer with 57 years of age. Because I was the only one who worked a little, back in the beginning, with C ++, my bosses asked me to serve a customer who needs a communication agent with very specific characteristics. It can run as a daemon on multiple platforms and be both client and server at times. I do not know enough but I have to solve the problem and found a chance in the Boost / Asio library.
I am new to Boost-Asio and reading the documentation I created a server and a TCP socket client that exchanges messages perfectly and two-way, full duplex.
I read several posts where they asked for the same things I want, but all the answers suggested full duplex as if that meant having a client and a server in the same program. And it's not. The definition of full duplex refers to the ability to write and read from the same connection and every TCP connection is full duplex by default.
I need to make two programs can accept connections initiated by the other. There will be no permanent connection between the two programs. Sometimes one of them will ask for a connection and at other times the other will make this request and both need to be listening, accepting the connection, exchanging some messages and terminating the connection until new request is made.
The server I did seems to get stuck in the process of listening to the port to see if a connection is coming in and I can not continue with the process to be able to create a socket and request a connection with the other program. I need threads but I do not know enough about them.
It'is possible?
As I said I'm new to Boost / Asio and I tried to follow some documents of threads and Coroutines. Then I put the client codes in one method and the server in another.:
int main(int argc, char* argv[])
{
try
{
boost::thread t1(&server_agent);
boost::thread t2(&client_agent);
// wait
t1.join();
t2.join();
return 0;
}
catch (std::exception& e)
{
std::cerr << "Exception: " << e.what() << "\n";
}
return 0;
}
and two Coroutines:
void client_agent() {
parameters param;
param.load();
boost::asio::io_service io_service1;
tcp::resolver resolver(io_service1);
char port[5];
_itoa(param.getNrPortaServComunic(), port, 10);
auto endpoint_iterator = resolver.resolve({ param.getIPServComunicPrincipal(), port });
std::list<client> clients;
client c(io_service1, endpoint_iterator, param);
while (true)
{
BOOL enviada = FALSE;
while (true) {
if (!enviada) {
std::cout << "sending a message\n";
int nr = 110;
message msg(nr, param);
c.write(msg);
enviada = TRUE;
}
}
}
c.close();
}
void server_agent() {
parameters param;
param.load();
boost::asio::io_service io_service1;
std::list<server> servers;
tcp::endpoint endpoint(tcp::v4(), param.getNrPortaAgenteServ());
servers.emplace_back(io_service1, endpoint);
io_service1.run();
}
I used one port to client endpoint and other port to server endpoint. Is it correct? Required?
It starts looking like it's going to work. Each of the methods runs concurrently but then I get a thread allocation error at the io_service1.run (last line of the server_agent method):
boost::exception_detail::clone_impl > at memory location 0x0118C61C.
Any suggestion?
You are describing a UDP client/server application. But your implementation is bound to fail. Think of an asio server or client as always running in a single thread.
The following code is just so you get an idea. I haven't tried to compile it. Client is very similar, but may need a transmit buffer, depends on the app, obviously.
This is a shortened version, so you get the idea. In a final application you way want to add receive timeouts and the likes. The same principles hold for TCP servers, with the added async_listen call. Connected sockets can be stored in shared_ptr, and captured by the lambdas, will destroy almost magically.
Server is basically the same, except there is no constant reading going on. If running both server and client in the same process, you can rely on run() to be looping because of the server, but if not, you'd have to call run() for each connection. run() would exit at the end of the exchange.
using namespace boost::asio; // Or whichever way you like to shorten names
class Server
{
public:
Server(io_service& ios) : ios_(ios) {}
void Start()
{
// create socket
// Start listening
Read();
}
void Read()
{
rxBuffer.resize(1024)
s_.async_receive_from(
buffer(rxBuffer),
remoteEndpoint_,
[this](error_code ec, size_t n)
{
OnReceive(ec, n); // could be virtual, if done this way
});
}
void OnReceive(error_code ec, size_t n)
{
rxBuffer_.resize(n);
if (ec)
{
// error ... stops listen loop
return;
}
// grab data, put in txBuffer_
Read();
s_.async_send_to(
buffer(txBuffer_),
remoteEndpoint_,
[this, msg](error_code ec, size_t n)
{
OnTransmitDone(ec, n);
});
}
void OnTransmitDone(error_code ec, size_t n)
{
// check for error?
txBuffer_.clear();
}
protected:
io_service& ios_;
ip::udp::socket s_;
ip::udp::endpoint remoteEndpoint_; // the other's address/port
std::vector<char> rxBuffer_; // could be any data type you like
std::vector<char> txBuffer_; // idem All access is in one thread, so only
// one needed for simple ask/respond ops.
};
int main()
{
io_service ios;
Server server(ios); // could have both server and client run on same thread
// on same io service this way.
Server.Start();
ios_run();
// or std::thread ioThread([&](){ ios_.run(); });
return 0;
}

Random EOF in boost asio in multi thread

I am quite new to boost asio and I am experiencing random End of File in a multi threaded server.
I could reproduce my problem in this small example:
Server:
This is a simple echo server. The protocol is straightforward :
(1) A client Connect
(2) The server reads one byte. This byte is the length of the string to read and send back.
(3) The server reads N bytes.
(4) The server send back N+1 bytes to the client and goes back to (2).
When the Client disconnect An EOF is captured in (3) and the handler loop stops.
class MySocket{
public:
char buffer[257];
boost::asio::ip::tcp::socket socket;
MySocket(boost::asio::io_service*ios):socket(*ios){}
~MySocket(){}
};
//Handlers
void readN(std::shared_ptr<MySocket>server,const boost::system::error_code&ec);
//(4)
void echo(std::shared_ptr<MySocket>server,const boost::system::error_code&ec){
if(ec){
throw std::exception(("This is NOT OK: "+ec.message()).c_str());}
size_t n=server->buffer[0]&0xFF;
std::cout<<std::string(server->buffer+1,n)<<std::endl;
boost::asio::async_write(server->socket,boost::asio::buffer(server->buffer,n+1),boost::bind(readN,server,boost::asio::placeholders::error));}
//(3)
void read(std::shared_ptr<MySocket>server,const boost::system::error_code&ec){
if(ec){
throw std::exception(("This is OK: "+ec.message()).c_str());}
size_t n=server->buffer[0]&0xFF;
boost::asio::async_read(server->socket,boost::asio::buffer(server->buffer+1,n),boost::bind(echo,server,boost::asio::placeholders::error));}
//(2)
void readN(std::shared_ptr<MySocket>server,const boost::system::error_code&ec){
if(ec){
throw std::exception(("This is also NOT OK: "+ec.message()).c_str());}
boost::asio::async_read(server->socket,boost::asio::buffer(server->buffer+0,1),boost::bind(read,server,boost::asio::placeholders::error));}
//Server
void serve(boost::asio::io_service*ios){
for(;;){
try{ios->run();break;}
catch(const std::exception&e){std::cout<<e.what()<<std::endl;}}}
//(1)
void accept(boost::asio::io_service*ios,boost::asio::ip::tcp::acceptor*acceptor,std::shared_ptr<MySocket>server,const boost::system::error_code&ec){
if(server.get()!=nullptr){
server->socket.set_option(boost::asio::ip::tcp::no_delay(true));
readN(server,ec);}
server.reset(new MySocket(ios));
acceptor->async_accept(server->socket,boost::bind(accept,ios,acceptor,server,boost::asio::placeholders::error));}
int main(){
boost::asio::io_service ios;
boost::asio::ip::tcp::acceptor acceptor(ios,boost::asio::ip::tcp::endpoint(boost::asio::ip::tcp::v4(),1207));
boost::asio::io_service::work work(ios);
accept(&ios,&acceptor,nullptr,boost::system::error_code());
// std::thread other(boost::bind(serve,&ios));
serve(&ios);
acceptor.close();
ios.stop();
// other.join();
return 0;}
Client:
The client is connecting once to the server and sending 1000 strings.
int main(){
try{
boost::asio::io_service ios;
boost::asio::ip::tcp::socket socket(ios);
boost::asio::ip::tcp::endpoint endpoint(boost::asio::ip::address::from_string("127.0.0.1"),1207);
socket.connect(endpoint);
socket.set_option(boost::asio::ip::tcp::no_delay(true));
char buf[257];
for(size_t i=0;i<1000;++i){
size_t n=(i%127)+1;
buf[0]=(char)n;
for(size_t j=0;j<n;++j){
buf[j+1]=(char)('A'+(j+i)%26);}
socket.send(boost::asio::buffer(buf,n+1));
socket.receive(boost::asio::buffer(buf,1));
if((buf[0]&0xFF)!=n){
throw std::exception("Oups!");}
socket.receive(boost::asio::buffer(buf+1,n));
for(size_t j=0;j<n;++j){
if(buf[j+1]!=(char)('A'+(j+i)%26)){
throw std::exception("Oups!");}}
std::cout<<i<<": "<<std::string(buf+1,n)<<std::endl;}}
catch(const std::exception&e){
std::cout<<e.what()<<std::endl;}
return 0;}
When The server uses only one thread (the tread other is commented) the server echos correctly the 1000 strings.
When The server uses the other thread, An EOF is captured in (4) after a random number of printed strings. This should never happen.
I tried wrapping all the async calls with a strand, but it did not work.
As far as I can see, there is no data race issue. Handlers should be called one after another.
What did I miss ?
What is the correct idiom to handle a multithreaded asio application ?
EDIT :
I did a few tests and it appears that if I replace this line
throw std::exception(("This is NOT OK: "+ec.message()).c_str());
with:
std::cout<<"This is not OK: "<<ec.message()<<std::endl;
The server echos correctly the 1000 lines even if I see that a few EOF were incorrectly passed as arguments a few times.
So I guess the question is why do I get an incorrect boost::asio::error::eof when the socket is obviously not closed ?
This is not what is stated here.
This is a bug of boost::asio 1.54.0
I found two similar threads on the internet:
this one.
and that one (on stack overflow).
There is also a bug report here.
I installed boost 1.53 and it is now working just fine.
This issue was a regression and has been fixed in Boost 1.55:
http://www.boost.org/users/history/version_1_55_0.html