Operation canceled boost asio async_receive_from - c++

I have an UDP Server set up with boost/asio (I copied the example and just changed a few things). Below is the code:
udp_server.hpp
using boost::asio::ip::udp;
class udp_server {
public:
udp_server(boost::asio::io_service&, int);
private:
boost::array<char, 256> recBuffer;
udp::socket socket_;
udp::endpoint remote_endpoint_;
void start_receive();
void handle_receive(const boost::system::error_code&, std::size_t);
void handle_send(boost::shared_ptr<std::string> /*message*/,
const boost::system::error_code& /*error*/,
std::size_t /*bytes_transferred*/)
{}
};
and udp_server.cpp
udp_server::udp_server( boost::asio::io_service& io_service,
int port)
: socket_(io_service, udp::endpoint(udp::v4(), port)) {
serverNotifications.push_back("UDP Server class initialized.");
start_receive();
}
void udp_server::start_receive() {
socket_.async_receive_from(
boost::asio::buffer(recBuffer),
remote_endpoint_,
boost::bind(&udp_server::handle_receive,
this,
boost::asio::placeholders::error,
boost::asio::placeholders::bytes_transferred));
serverNotifications.push_back("Starting to receive UDP Messages.");
}
void udp_server::handle_receive(const boost::system::error_code& error,
std::size_t size) {
serverNotifications.push_back("RecFrom: " + remote_endpoint_.address().to_string());
if (!error) {
// I do data stuff here
} else {
errors.push_back("Handle Receive error: " + error.message());
}
}
After initializing the Server with:
try {
udp_server server(io_service, ApplData.PORT, (size_t)ApplData.BUFLEN);
} catch (std::exception& e) {
// error handling
}
and running it with io_service.run() in a seperate try catch in another function I get some problems:
My Callback function handle_receive gets called without any UDP message getting send in the whole network (aka only my laptop without connection)
error.message() returns "Operation canceled"
remote_endpoint_.address().to_string() returns "acfc:4000:0:0:7800::%2885986016" which I can't identify as something useful
Also I recognized that my io_service is stopping all the time, but in my understanding it should run all the time, right?
I already thought about referencing this in the callback function bind with a shared_from_this ptr, but since I have a real instance of the udp_server class until I leave my program I can't think of a good reason to do that.
Can someone explain thy this failure occurs, what these errors tell me about my code or what I can do to avoid them?

Nevermind, Rubberduck debugging was enough. I just read the line
but since I have a real instance of the udp_server class until I leave my program I can't think of a good reason to do that.
and noticed, that I actually didn't have this and this was the error.

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:

Udp server from boost not working on multithreading, but work only on the main thread

I got a async udp server with boost::asio
but the problem is:
if I launch it on a thread, the server won't work
but if I launch it on the main thread (blocking with the service) it's working...
I've try to do it with a fork but not working eiser
class Server {
private:
boost::asio::io_service _IO_service;
boost::shared_ptr<boost::asio::ip::udp::socket> _My_socket;
boost::asio::ip::udp::endpoint _His_endpoint;
boost::array<char, 1000> _My_Buffer;
private:
void Handle_send(const boost::system::error_code& error, size_t size, std::string msg) {
//do stuff
};
void start_send(std::string msg) {
_My_socket->async_send_to(boost::asio::buffer(msg), _His_endpoint,
boost::bind(&Server::Handle_send, this,
boost::asio::placeholders::error,
boost::asio::placeholders::bytes_transferred, msg));
};
void Handle_receive(const boost::system::error_code& error, size_t size) {
//do stuff
};
void start_receive(void) {
_My_socket->async_receive_from(
boost::asio::buffer(_My_Buffer), _His_endpoint,
boost::bind(&Server::Handle_receive, this,
boost::asio::placeholders::error,
boost::asio::placeholders::bytes_transferred));
}
public:
Server(int port):
_IO_service(),
_My_socket(boost::make_shared<boost::asio::ip::udp::socket>(_IO_service, \
boost::asio::ip::udp::endpoint(boost::asio::ip::udp::v4(), port)))
{
start_receive();
};
void Launch() {
_IO_service.run();
};
};
the objective is to call the Server::Launch in the background.
First of all you have undefined behaviour in start_send.
async_send_to returns immediately, so msg as local variable is destroyed when start_send returns. When you call async_send_to you must ensure that msg is not destroyed before asynchronous operation is completed. What is described in documentation -
Although the buffers object may be copied as necessary, ownership of
the underlying memory blocks is retained by the caller, which must
guarantee that they remain valid until the handler is called.
You can resolve it by many ways, the easiest is to use string as data members (as buffer for sending data):
class Server {
//..
std::string _M_toSendBuffer;
//
void start_send(std::string msg) {
_M_toSend = msg; // store msg into buffer for sending
_My_socket->async_send_to(boost::asio::buffer(_M_toSend), _His_endpoint,
boost::bind(&Server::Handle_send, this,
boost::asio::placeholders::error,
boost::asio::placeholders::bytes_transferred,
_M_toSend));
};
Another solution is to wrap msg into smart pointer to extend its lifetime:
void Handle_send(const boost::system::error_code& error, size_t size,
boost::shared_ptr<std::string> msg) {
//do stuff
};
void start_send(std::string msg) {
boost::shared_ptr<std::string> msg2 = boost::make_shared<std::string>(msg); // [1]
_My_socket->async_send_to(boost::asio::buffer(*msg2), _His_endpoint,
boost::bind(&Server::Handle_send, this,
boost::asio::placeholders::error,
boost::asio::placeholders::bytes_transferred,
msg2)); // [2]
};
in [1] line we create shared_ptr which taks msg content, then in [2] line reference counter of shared_ptr is increased when bind is called, so string lifetime is extended and it is destroyed after handler is called.
Regarding your not-working verion based on thread. You didn't show the code where Launch is called, but maybe you just don't join this thread?
Server s(3456);
boost::thread th(&Server::Launch,&s);
th.join(); // are you calling this line?
or perhaps your code doesn't work by UB in start_send.

Boost Asio delayed write tcp socket

I have a simple TCP server with one thread with the asio loop, and a thread pool to do the computation. I'm able to listen to connections write something to it in the main thread. But I can't wait the answer of the worker thread because the connection is immediately closed after getting accepted.
I tried using a deadline timer but for some reason it gets called immediately with "Aborted operation" error.
The whole process I want to achieve is:
Accept connection
write something
send a task to the worker pool
wait for the answer from the worker (I'm using a thread safe queue to read message from the worker pool)
write the answer to the socket
close connection
Here is my code
class tcp_connection
: public boost::enable_shared_from_this<tcp_connection>
{
public:
typedef boost::shared_ptr<tcp_connection> pointer;
static pointer create(boost::asio::io_service& io_service)
{
return pointer(new tcp_connection(io_service));
}
tcp::socket& socket()
{
return socket_;
}
void start()
{
message_ = "Write me in 5 sec";
boost::asio::deadline_timer t(service_, boost::posix_time::seconds(5));
t.async_wait(boost::bind(&tcp_connection::writeAfter, shared_from_this(), boost::asio::placeholders::error));
}
private:
tcp_connection(boost::asio::io_service& io_service)
: service_(io_service), socket_(io_service)
{
}
void writeAfter(const boost::system::error_code&) {
std::cout << "writing to socket" << std::endl;
boost::asio::async_write(socket_, boost::asio::buffer(message_),
boost::bind(&tcp_connection::handle_write, shared_from_this(),
boost::asio::placeholders::error,
boost::asio::placeholders::bytes_transferred));
}
void handle_write(const boost::system::error_code& /*error*/,
size_t /*bytes_transferred*/)
{
}
boost::asio::io_service &service_;
tcp::socket socket_;
std::string message_;
};
EDIT: Debug log
#asio|1462018696.996630|0*1|deadline_timer#0x7ffd9dd40228.async_wait
#asio|1462018696.996675|0|deadline_timer#0x7ffd9dd40228.cancel
#asio|1462018696.996694|0*2|socket#0x7ffd9dd403e0.async_accept
#asio|1462018696.996714|0*3|deadline_timer#0x7ffd9dd40408.async_wait
#asio|1462018696.996736|>1|ec=system:125
As we can see the cancel is called on the timer but I don't have a single cancel in my code so I don't know why it is called.
Thank you very much for your help
Are you still listening for other connections after you create a tcp_connection?
Since you haven't called async_read or async_read_some for your new connection, io_service.run() for that thread may simply have completed...
If you start the deadline timer in the tcp_connection constructor, it should keep io_service.run() going and send the message.
I found why.
There was a mess with shared pointers and my connection object was destroyed. So was the TCP object. The connection was close.
Thanks #kenba for your help !

Correct use of Boost::asio inside of a separate thread

I am writing a DLL plugin for the Orbiter space simulator, which allows for UDP communication with an external system. I've chosen boost::asio for the task, as it allows me to abstract from the low-level stuff.
The "boundary conditions" are as follows:
I can create any threads or call any API functions from my DLL
I can modify the data inside of the simulation only inside the callback passed to my DLL (each frame), due to lack of other thread safety.
Hence, I chose the following architecture for the NetworkClient class I'm using for communications:
Upon construction, it initializes the UDP socket (boost::socket+boost::io_service) and starts a thread, which calls io_service.run()
Incoming messages are put asyncronously into a queue (thread-safe via CriticalSection)
The callback processing function can pull the messages from queue and process it
However, I have run into some strange exception upon running the implementation:
boost::exception_detail::clone_impl > at memory location 0x01ABFA00.
The exception arises in io_service.run() call.
Can anyone point me, please, am I missing something? The code listings for my classes are below.
NetworkClient declaration:
class NetworkClient {
public:
NetworkClient(udp::endpoint server_endpoint);
~NetworkClient();
void Send(shared_ptr<NetworkMessage> message);
inline bool HasMessages() {return incomingMessages.HasMessages();};
inline shared_ptr<NetworkMessage> GetQueuedMessage() {return incomingMessages.GetQueuedMessage();};
private:
// Network send/receive stuff
boost::asio::io_service io_service;
udp::socket socket;
udp::endpoint server_endpoint;
udp::endpoint remote_endpoint;
boost::array<char, NetworkBufferSize> recv_buffer;
// Queue for incoming messages
NetworkMessageQueue incomingMessages;
void start_receive();
void handle_receive(const boost::system::error_code& error, std::size_t bytes_transferred);
void handle_send(boost::shared_ptr<std::string> /*message*/, const boost::system::error_code& /*error*/, std::size_t /*bytes_transferred*/) {}
void run_service();
NetworkClient(NetworkClient&); // block default copy constructor
};
Methods implementation:
NetworkClient::NetworkClient(udp::endpoint server_endpoint) : socket(io_service, udp::endpoint(udp::v4(), 28465)) {
this->server_endpoint = server_endpoint;
boost::thread* th = new boost::thread(boost::bind(&NetworkClient::run_service,this));
start_receive();
}
void NetworkClient::start_receive()
{
socket.async_receive_from(boost::asio::buffer(recv_buffer), remote_endpoint,
boost::bind(&NetworkClient::handle_receive, this, boost::asio::placeholders::error, boost::asio::placeholders::bytes_transferred)
);
}
void NetworkClient::run_service()
{
this->io_service.run();
}
There's nothing wrong with your architecture that I can see. You should catch exceptions thrown from io_service::run(), that is likely the source of your problem.
void NetworkClient::run_service()
{
while(1) {
try {
this->io_service.run();
} catch( const std::exception& e ) {
std::cerr << e.what << std::endl;
}
}
}
You'll also want to fix whatever is throwing the exception.

Triggering writes with Boost::asio

I have some software that I would like to make a TCP client. I don't know if this is the best architecture, but in my software I spawn a thread that will be used for the Network I/O. If there is a better architecture, I'd appreciate some pointers and advice.
Both threads have a refernce to the boost::asio::io_service object and a Session object that encapsulates the socket object. The sesson object is roughly as follows:
class Session
{
public:
Session(
boost::asio::io_service & io_service,
std::string const & ip_address,
std::string const & port)
: io_service_(io_service),
resolver_(io_service),
socket_(io_service),
ip_address_(ip_address),
port_(port),
{}
virtual void start();
virtual ~Session();
virtual void stop();
void write(std::string const & msg);
void handle_resolve(
const boost::system::error_code & error,
boost::asio::ip::tcp::resolver::iterator endpoint_itr);
void handle_connect(
const boost::system::error_code & error,
boost::asio::ip::tcp::resolver::iterator endpoint_itr);
void handle_close();
void handle_write(const boost::system::error_code & error);
private:
boost::asio::io_service & io_service_;
boost::asio::ip::tcp::resolver resolver_;
boost::asio::ip::tcp::socket socket_;
std::string ip_address_;
std::string port_;
};
In the I/O thread run-loop, the start() method of the session object is called which connects to the server. (This works, btw). Then, the thread sits in a loop calling the run() method on the I/O service object [io_service_.run()] to trigger events.
The main thread calls the write() method of the session when it wants to send data, and the session object calls boost::async_write with the data to write and then a callback method that is a member of the session object (handle_write).
While I have the I/O thread connecting to the server, I cannot get the handle_write method to be triggered. I have verified that the main thread is calling into the session object and executing async_write() on the socket. It is just that the callback is never triggered. I also don't see any data on the server side or over the wire with tcpdump.
Any idea where my problem might be? Is there a better way to organize the architecture? Most of all, I don't want to block the main thread doing I/O.
Here is the code that spawns the io thread from the main thread (apologies for the spacing):
boost::asio::io_service io_service;
boost::shared_ptr<Session> session_ptr;
boost::thread io_thread;
....
session_ptr.reset(
new Session::Session(
io_service,
std::string("127.0.0.1"),
std::string("17001")));
// spawn new thread for the network I/O endpoint
io_thread = boost::thread(
boost::bind(
&Session::start,
session_ptr_.get()));
The code for the start() method is as follows:
void Session::start()
{
typedef boost::asio::ip::tcp tcp;
tcp::resolver::query query(
tcp::v4(),
ip_address_,
port_);
resolver_.async_resolve(
query,
boost::bind(
&Session::handle_resolve,
this,
boost::asio::placeholders::error,
boost::asio::placeholders::iterator));
while(1){ // improve this later
io_service_.run();
}
}
The callback for the resolver:
void Session::handle_resolve(
const boost::system::error_code & error,
boost::asio::ip::tcp::resolver::iterator endpoint_itr)
{
if (!error)
{
boost::asio::ip::tcp::endpoint endpoint = *endpoint_itr;
socket_.async_connect(
endpoint,
boost::bind(
&Session::handle_connect,
this,
boost::asio::placeholders::error,
++endpoint_itr));
}
else
{
std::cerr << "Failed to resolve\n";
std::cerr << "Error: " << error.message() << std::endl;
}
}
The callback for connect:
void Session::handle_connect(
const boost::system::error_code & error,
boost::asio::ip::tcp::resolver::iterator endpoint_itr)
{
typedef boost::asio::ip::tcp tcp;
if (!error)
{
std::cerr << "Connected to the server!\n";
}
else if (endpoint_itr != tcp::resolver::iterator())
{
socket_.close();
socket_.async_connect(
*endpoint_itr,
boost::bind(
&Session::handle_connect,
this,
boost::asio::placeholders::error,
++endpoint_itr));
}
else
{
std::cerr << "Failed to connect\n";
}
}
The write() method that the main thread can call to send post an asychronous write.
void Session::write(
std::string const & msg)
{
std::cout << "Write: " << msg << std::endl;
boost::asio::async_write(
socket_,
boost::asio::buffer(
msg.c_str(),
msg.length()),
boost::bind(
&Session::handle_write,
this,
boost::asio::placeholders::error));
}
And finally, the write completion callback:
void Session::handle_write(
const boost::system::error_code & error)
{
if (error)
{
std::cout << "Write complete with errors !!!\n";
}
else
{
std::cout << "Write complete with no errors\n";
}
}
Looks like your io service will run out of work after connect, after which you just call io_service::run again? It looks like run is being called in the while loop, however I can't see a call to reset anywhere. You need to call io::service::reset before you call run on the same io_service again.
Structurally, it would be better to add work to the io_service, then you don't need to call it in the loop and the run will exit once you call io_service::stop.
this portion of your code
boost::asio::io_service io_service;
boost::shared_ptr<Session> session_ptr;
boost::thread io_thread;
....
session_ptr.reset(
new Session::Session(
io_service,
std::string("127.0.0.1"),
std::string("17001")));
// spawn new thread for the network I/O endpoint
io_thread = boost::thread(
boost::bind(
&Session::start,
session_ptr_.get()));
is a red flag to me. Your io_service object is possibly going out of scope and causing strange behavior. An io_service is not copyable, so passing it to your Session as a non-const reference is probably not what you are hoping to achieve.
samm#macmini ~> grep -C 2 noncopyable /usr/include/boost/asio/io_service.hpp
#include <boost/asio/detail/epoll_reactor_fwd.hpp>
#include <boost/asio/detail/kqueue_reactor_fwd.hpp>
#include <boost/asio/detail/noncopyable.hpp>
#include <boost/asio/detail/select_reactor_fwd.hpp>
#include <boost/asio/detail/service_registry_fwd.hpp>
--
*/
class io_service
: private noncopyable
{
private:
--
/// Class used to uniquely identify a service.
class io_service::id
: private noncopyable
{
public:
--
/// Base class for all io_service services.
class io_service::service
: private noncopyable
{
public:
If you're basing your code off the HTTP client example, you should note the io_service is in scope all the time inside of main(). As Ralf pointed out, your io_service is also likely running out of work to do after the connect handler, which is why you've kludged it to invoke run() inside of a loop
while(1){ // improve this later
io_service_.run();
}
again, note that the HTTP client example does not do this. You need to start another async operation inside of the connect handler, either a read or write depending on what your application needs.