Initializing boost::asio socket after constructor failed - c++

I create a class to broadcast UDP messages as follows:
#define _CRT_SECURE_NO_WARNINGS
#include <ctime>
#include <iostream>
#include <string>
#include <queue>
#include <boost/array.hpp>
#include <boost/bind.hpp>
#include <boost/shared_ptr.hpp>
#include <boost/asio.hpp>
#include <boost/lexical_cast.hpp>
#include <boost/thread.hpp>
#include <boost/thread/thread.hpp>
#include <boost/chrono.hpp>
#include <boost/date_time/posix_time/posix_time.hpp>
using boost::asio::ip::udp;
using std::cout;
using std::cin;
using std::endl;
using std::string;
using namespace std;
template<typename T>
std::string toString(const T& value);
std::string IntToString(const int& i);
class UdpCore
{
private:
boost::asio::ip::udp::endpoint endpoint;
boost::asio::ip::udp::socket socket;
string multicast_address;
unsigned short multicast_port;
boost::thread_group threads; // thread group
boost::thread* thread_main; // main thread
boost::thread* thread_listen; // listen thread
boost::thread* thread_getsend; // get/send thread
boost::mutex stopMutex;
bool initialize = false;
bool stop, showBroadcast;
int i_getsend, i_listen, i_main, i_message, interval;
string message;
public:
// constructor
UdpCore(boost::asio::io_service& io_service, std::string multicast_address, unsigned short multicast_port, int interval, bool show = false)
: endpoint(boost::asio::ip::address::from_string(multicast_address), multicast_port),
socket(io_service, endpoint.protocol()),
multicast_address(multicast_address),
multicast_port(multicast_port),
interval(interval),
showBroadcast(show)
{
Initialize(io_service, show);
}
~UdpCore()
{
// show exit message
cout << "Exiting UDP Core." << endl;
}
// initialize
void Initialize(boost::asio::io_service& io_service, bool show = false)
{
if (initialize == false)
{
GetInfo();
}
boost::asio::ip::udp::endpoint endpoint(boost::asio::ip::make_address(multicast_address), multicast_port);
boost::asio::ip::udp::socket socket(io_service, endpoint.protocol());
socket.set_option(boost::asio::ip::udp::socket::reuse_address(true)); // no need
thread_main = new boost::thread(boost::ref(*this));
thread_getsend = new boost::thread(&UdpCore::Callable_GetSend, this, interval, boost::ref(i_listen), boost::ref(message));
threads.add_thread(thread_getsend); // get/send thread
stop = false;
showBroadcast = show;
i_getsend = 0;
i_listen = 0;
i_main = 0;
i_message = 0;
message.clear();
initialize = true;
}
void GetInfo()
{
multicast_address = "192.168.0.255";
multicast_port = 13000;
interval = 500;
}
// start the threads
void Start()
{
// Wait till they are finished
threads.join_all();
}
// stop the threads
void Stop()
{
// warning message
cout << "Stopping all threads." << endl;
// signal the threads to stop (thread-safe)
stopMutex.lock();
stop = true;
stopMutex.unlock();
// wait for the threads to finish
thread_main->interrupt(); // in case not interrupted by operator()
threads.interrupt_all();
threads.join_all();
// close socket after everything closes
socket.close();
}
void Callable_Listen(int interval, int& count)
{
while (!stop)
{
if (message != "")
socket.async_send_to(boost::asio::buffer(message), endpoint, [this](boost::system::error_code ec, std::size_t /*length*/)
{
stopMutex.lock();
if (showBroadcast)
{
cout << i_message << " - " << message << endl; // show count
}
message.clear(); //clear after sending
stopMutex.unlock();
});
++i_message;
// wait routine
boost::this_thread::sleep(boost::posix_time::millisec(interval));
boost::this_thread::interruption_point();
++i_listen;
}
}
void Callable_GetSend(int interval, int& count, string& userInput)
{
while (!stop)
{
stopMutex.lock();
cout << "Callable_GetSend [" << count++ << "]. Enter message: ";
getline(cin, userInput);
if (message != "")
socket.async_send_to(boost::asio::buffer(message), endpoint, [this](boost::system::error_code ec, std::size_t /*length*/)
{
if (showBroadcast)
{
cout << i_message << " - " << message << endl; // show count
}
message.clear(); //clear after sending
});
stopMutex.unlock();
// wait routine
boost::this_thread::sleep(boost::posix_time::millisec(interval));
boost::this_thread::interruption_point();
++i_getsend;
++i_message;
}
}
// Thread function
void operator () ()
{
while (!stop)
{
if (message == "STOP")
{
try
{
this->Stop();
}
catch (exception e)
{
cout << e.what() << endl;
}
}
boost::this_thread::sleep(boost::posix_time::millisec(interval));
boost::this_thread::interruption_point();
}
}
std::string make_daytime_string()
{
using namespace std; // For time_t, time and ctime;
time_t now = time(0);
std::string result = ctime(&now);
return result.erase(result.length() - 1, 1);
}
std::string some_string()
{
std::string result;
result = make_daytime_string();
return result;
}
};
int main()
{
try
{
boost::asio::io_service io_service;
UdpCore mt(io_service, "192.168.0.255", 13000, 5000, false);
mt.Start();
}
catch (std::exception& e)
{
std::cerr << "Exception: " << e.what() << "\n";
}
}
It works fine. However, I wish to initialize the socket after the class constructor is initialized (to allow user input). Thus, I change from using socket to a socket pointer, as suggested by this post
. The modified code is as follows:
#define _CRT_SECURE_NO_WARNINGS
#include <ctime>
#include <iostream>
#include <string>
#include <queue>
#include <boost/array.hpp>
#include <boost/bind.hpp>
#include <boost/shared_ptr.hpp>
#include <boost/asio.hpp>
#include <boost/lexical_cast.hpp>
#include <boost/thread.hpp>
#include <boost/thread/thread.hpp>
#include <boost/chrono.hpp>
#include <boost/date_time/posix_time/posix_time.hpp>
using boost::asio::ip::udp;
using std::cout;
using std::cin;
using std::endl;
using std::string;
using namespace std;
template<typename T>
std::string toString(const T& value);
std::string IntToString(const int& i);
class UdpCore
{
private:
boost::asio::ip::udp::endpoint endpoint;
boost::shared_ptr<udp::socket> socketPtr;
string multicast_address;
unsigned short multicast_port;
boost::thread_group threads; // thread group
boost::thread* thread_main; // main thread
boost::thread* thread_listen; // listen thread
boost::thread* thread_getsend; // get/send thread
boost::mutex stopMutex;
bool initialize = false;
bool stop, showBroadcast;
int i_getsend, i_listen, i_main, i_message, interval;
string message;
public:
// constructor
UdpCore(boost::asio::io_service& io_service, std::string multicast_address, unsigned short multicast_port, int interval, bool show = false)
: multicast_address(multicast_address),
multicast_port(multicast_port),
interval(interval),
showBroadcast(show)
{
UdpCore(io_service, show);
}
UdpCore(boost::asio::io_service& io_service, bool show = false)
: showBroadcast(show)
{
Initialize(io_service, show);
}
// destructor
~UdpCore()
{
// show exit message
cout << "Exiting UDP Core." << endl;
}
// initialize
void Initialize(boost::asio::io_service& io_service, bool show = false)
{
if (initialize == false)
{
GetInfo();
}
boost::asio::ip::udp::endpoint endpoint(boost::asio::ip::address::from_string(multicast_address), multicast_port);
socketPtr = boost::make_shared<udp::socket>(boost::ref(io_service), endpoint.protocol());
socketPtr->set_option(boost::asio::ip::udp::socket::reuse_address(true)); // no need
thread_main = new boost::thread(boost::ref(*this));
thread_getsend = new boost::thread(&UdpCore::Callable_GetSend, this, interval, boost::ref(i_listen), boost::ref(message));
threads.add_thread(thread_getsend); // get/send thread
stop = false;
showBroadcast = show;
i_getsend = 0;
i_listen = 0;
i_main = 0;
i_message = 0;
message.clear();
initialize = true;
}
void GetInfo()
{
multicast_address = "192.168.0.255";
multicast_port = 13000;
interval = 500;
}
// start the threads
void Start()
{
// Wait till they are finished
threads.join_all();
}
// stop the threads
void Stop()
{
// warning message
cout << "Stopping all threads." << endl;
// signal the threads to stop (thread-safe)
stopMutex.lock();
stop = true;
stopMutex.unlock();
// wait for the threads to finish
thread_main->interrupt(); // in case not interrupted by operator()
threads.interrupt_all();
threads.join_all();
// close socket after everything closes
socketPtr->close();
}
void Callable_Listen(int interval, int& count)
{
while (!stop)
{
if (message != "")
socketPtr->async_send_to(boost::asio::buffer(message), endpoint, [this](boost::system::error_code ec, std::size_t /*length*/)
{
stopMutex.lock();
if (showBroadcast)
{
cout << i_message << " - " << message << endl; // show count
}
message.clear(); //clear after sending
stopMutex.unlock();
});
++i_message;
// wait routine
boost::this_thread::sleep(boost::posix_time::millisec(interval));
boost::this_thread::interruption_point();
++i_listen;
}
}
void Callable_GetSend(int interval, int& count, string& userInput)
{
while (!stop)
{
stopMutex.lock();
cout << "Callable_GetSend [" << count++ << "]. Enter message: ";
getline(cin, userInput);
if (message != "")
socketPtr->async_send_to(boost::asio::buffer(message), endpoint, [this](boost::system::error_code ec, std::size_t /*length*/)
{
if (showBroadcast)
{
cout << i_message << " - " << message << endl; // show count
}
message.clear(); //clear after sending
});
stopMutex.unlock();
// wait routine
boost::this_thread::sleep(boost::posix_time::millisec(interval));
boost::this_thread::interruption_point();
++i_getsend;
++i_message;
}
}
// Thread function
void operator () ()
{
while (!stop)
{
if (message == "STOP")
{
try
{
this->Stop();
}
catch (exception e)
{
cout << e.what() << endl;
}
}
boost::this_thread::sleep(boost::posix_time::millisec(interval));
boost::this_thread::interruption_point();
}
}
std::string make_daytime_string()
{
using namespace std; // For time_t, time and ctime;
time_t now = time(0);
std::string result = ctime(&now);
return result.erase(result.length() - 1, 1);
}
std::string some_string()
{
std::string result;
result = make_daytime_string();
return result;
}
};
int main()
{
try
{
boost::asio::io_service io_service;
UdpCore mt(io_service, false);
mt.Start();
}
catch (std::exception& e)
{
std::cerr << "Exception: " << e.what() << "\n";
}
}
where I have these replacements:
//boost::asio::ip::udp::socket socket;
boost::shared_ptr<udp::socket> socketPtr;
//boost::asio::ip::udp::socket socket(io_service, endpoint.protocol());
//socket.set_option(boost::asio::ip::udp::socket::reuse_address(true));
socketPtr = boost::make_shared<udp::socket>(boost::ref(io_service), endpoint.protocol());
socketPtr->set_option(boost::asio::ip::udp::socket::reuse_address(true));
//socket.async_send_to(boost::asio::buffer(message), endpoint, [this](boost::system::error_code ec, std::size_t /*length*/)
socketPtr->async_send_to(boost::asio::buffer(message), endpoint, [this](boost::system::error_code ec, std::size_t /*length*/)
Just one thing with the modification: it doesn't work. I keep poring over the code, and could not find the reason why it shouldn't work. Could someone please help?

In the first sample, besides the member endpoint and socket, you create a local endpoint and socket in Initialize() function. The latter objects are just unused - however, the member endpoint is properly initialized.
On the other hand, in the second example you do not properly initialize the member endpoint. Instead, you create again a local one. But pay attention that the endpoint used throughout the code is the member one.
The bottom line: remove the local objects and add correct initialization for the members.

Related

Asio async_read_until EOF Error in Asynchronous TCP Server

when I build it, and running server and then run client, that appear a error
error code = 2, error message = End of file
when I code synchronous tcp server it's work ok;
thanks
full client code
#include <boost/predef.h> // Tools to identify the os
#ifdef BOOST_OS_WINDOWS
#define _WIN32_WINNT 0x0501
#if _WIN32_WINNT <= 0x0502
#define BOOST_ASIO_DISABLE_TOCP
#define BOOST_ASIO_ENABLE_CANCELIO
#endif
#endif
#include <boost/asio.hpp>
#include <mutex>
#include <thread>
#include <memory>
#include <iostream>
#include <map>
using namespace boost;
typedef void(*Callback) (unsigned int request_id, const std::string& response, const system::error_code& ec);
struct Session{
Session(asio::io_service& ios, const std::string& raw_ip_address, unsigned short port_num, const std::string& request, unsigned int id, Callback callback) : m_sock(ios), m_ep(asio::ip::address::from_string(raw_ip_address),port_num), m_request(request), m_id(id), m_callback(callback), m_was_cancelled(false) {}
asio::ip::tcp::socket m_sock;
asio::ip::tcp::endpoint m_ep; // Remote endpoint
std::string m_request;
// streambuf where the response will be stored.
asio::streambuf m_response_buf;
std::string m_response; // Response represented as a string
system::error_code m_ec;
unsigned int m_id;
Callback m_callback;
bool m_was_cancelled;
std::mutex m_cancel_guard;
};
class AsyncTCPClient : public boost::asio::noncopyable {
public:
AsyncTCPClient(){
m_work.reset(new boost::asio::io_service::work(m_ios));
m_thread.reset(new std::thread([this](){
m_ios.run();
}));
}
void emulateLongComputationOp( unsigned int duration_sec, const std::string& raw_ip_address, unsigned short port_num, Callback callback, unsigned int request_id){
std::string request = "EMULATE_LONG_CALC_OP " + std::to_string(duration_sec) + "\n";
std::cout << "Request: " << request << std::endl;
std::shared_ptr<Session> session = std::shared_ptr<Session> (new Session(m_ios, raw_ip_address, port_num, request, request_id, callback));
session->m_sock.open(session->m_ep.protocol());
// active sessions list can be accessed from multiple thread, we guard it with a mutex to avoid data coruption
std::unique_lock<std::mutex> lock(m_active_sessions_guard);
m_active_sessions[request_id] = session;
lock.unlock();
session->m_sock.async_connect(session->m_ep, [this, session](const system::error_code& ec) {
if (ec.value() != 0) {
session->m_ec = ec;
onRequestComplete(session);
return;
}
std::unique_lock<std::mutex> cancel_lock(session->m_cancel_guard);
if (session->m_was_cancelled) {
onRequestComplete(session);
return;
}
asio::async_write(session->m_sock, asio::buffer(session->m_request), [this, session](const boost::system::error_code &ec, std::size_t bytes_transferred) {
if (ec.value() != 0) {
session->m_ec = ec;
onRequestComplete(session);
return;
}
std::unique_lock<std::mutex> cancel_lock(session->m_cancel_guard);
if (session->m_was_cancelled) {
onRequestComplete(session);
return;
}
asio::async_read_until(session->m_sock, session->m_response_buf, '\n',
[this, session](const boost::system::error_code &ec,
std::size_t bytes_transferred) {
if (ec.value() != 0) {
session->m_ec = ec;
} else {
std::istream strm(&session->m_response_buf);
std::getline(strm, session->m_response);
}
onRequestComplete(session);
});
});
});
};
// Cancels the request
void cancelRequest(unsigned int request_id){
std::unique_lock<std::mutex> lock(m_active_sessions_guard);
auto it = m_active_sessions.find(request_id);
if(it != m_active_sessions.end()){
std::unique_lock<std::mutex> cancel_lock(it->second->m_cancel_guard);
it->second->m_was_cancelled = true;
it->second->m_sock.cancel();
}
}
void close(){
// Destroy work object
m_work.reset(NULL);
// wait for the I/O thread tot exit
m_thread->join();
}
private:
void onRequestComplete(std::shared_ptr<Session> session){
// shutting down the connection, we don't care about the error code if function failed
boost::system::error_code ignored_ec;
session->m_sock.shutdown(asio::ip::tcp::socket::shutdown_both, ignored_ec);
// remove session from the map of active sessions
std::unique_lock<std::mutex> lock(m_active_sessions_guard);
auto it = m_active_sessions.find(session->m_id);
if(it != m_active_sessions.end()){
m_active_sessions.erase(it);
}
lock.unlock();
boost::system::error_code ec;
if(session->m_ec.value() == 0 && session->m_was_cancelled){
ec = asio::error::operation_aborted;
}else{
ec = session->m_ec;
}
session->m_callback(session->m_id, session->m_response, ec);
};
private:
asio::io_service m_ios;
std::map<int, std::shared_ptr<Session>> m_active_sessions;
std::mutex m_active_sessions_guard;
std::unique_ptr<boost::asio::io_service::work> m_work;
std::unique_ptr<std::thread> m_thread;
};
void handler(unsigned int request_id, const std::string& response, const system::error_code& ec){
if(ec.value() == 0){
std::cout << "Request #" << request_id << " has completed. Reponse: "<< response << std::endl;
}else if(ec == asio::error::operation_aborted){
std::cout << "Request #" << request_id << " has been cancelled by the user. " << std::endl;
}else{
std::cout << "Request #" << request_id << " failed! Error code = " << ec.value() << ". Error Message = " << ec.message() << std::endl;
}
return;
}
int main(){
try{
AsyncTCPClient client;
// emulate the user's behavior
client.emulateLongComputationOp(10, "127.0.0.1", 3333, handler, 1);
std::this_thread::sleep_for(std::chrono::seconds(60));
// another request with id 2
client.emulateLongComputationOp(11, "127.0.0.1", 3334, handler, 2);
// cancel request 1
client.cancelRequest(1);
std::this_thread::sleep_for(std::chrono::seconds(6));
// another request with id 3
client.emulateLongComputationOp(12, "127.0.0.1", 3335, handler, 3);
std::this_thread::sleep_for(std::chrono::seconds(15));
// exit the application
client.close();
}
catch(system::system_error &e){
std::cout << "Error occured! Error code = " << e.code() << ". Message: " << e.what();
return e.code().value();
}
return 0;
}
full server code
#include <boost/asio.hpp>
#include <thread>
#include <atomic>
#include <memory>
#include <iostream>
using namespace boost;
class Service {
public:
Service(std::shared_ptr<asio::ip::tcp::socket> sock) : m_sock(sock) {}
void StartHandling() {
asio::async_read_until(*m_sock.get(), m_request, '\n', [this](const boost::system::error_code& ec, std::size_t bytes_transferred){
onRequestReceived(ec, bytes_transferred);
});
std::istream is(&m_request);
std::string line;
std::getline(is, line);
std::cout << "m_request: " << line << std::endl;
}
private:
void onRequestReceived(const boost::system::error_code& ec, std::size_t bytes_transfered){
std::cout << "ec.value : " << ec.value() << std::endl;
if (ec.value() != 0){
std::cout << "Error occurred! Error code = " << ec.value() << ".Message: " << ec.message();
onFinish();
return;
}
// Process the request
asio::async_write(*m_sock.get(), asio::buffer(m_response), [this](const boost::system::error_code& ec, std::size_t bytes_transferred){
onResponseSent(ec, bytes_transferred);
});
}
void onResponseSent(const boost::system::error_code& ec, std::size_t bytes_transferred){
if(ec.value() != 0){
std::cout << "Error occurred! Error code = " << ec.value() << ". Message: " << ec.message();
}
onFinish();
}
// cleanup
void onFinish(){
delete this;
}
std::string ProcessingRequest(asio::streambuf& request){
// parse the request, process it and prepare the request
// Emulating CPU-consuming operations
int i = 0;
while (i != 1000){
i++;
std::this_thread::sleep_for(std::chrono::milliseconds(100));
}
std::string response = "Response\n";
return response;
}
std::shared_ptr<asio::ip::tcp::socket> m_sock;
std::string m_response;
asio::streambuf m_request;
};
class Acceptor {
public:
Acceptor(asio::io_service& ios, unsigned short port_num) : m_ios(ios), m_acceptor(m_ios, asio::ip::tcp::endpoint(asio::ip::address_v4::any(), port_num)), m_isStopped(
false) {}
// Start accepting incoming connection request.
void Start(){
m_acceptor.listen();
InitAccept();
}
void Stop() {
m_isStopped.store(true);
}
private:
void InitAccept() {
std::shared_ptr<asio::ip::tcp::socket> sock(new asio::ip::tcp::socket(m_ios));
m_acceptor.async_accept(*sock.get(), [this, sock](const boost::system::error_code& error){
onAccept(error, sock);
});
}
void onAccept(const boost::system::error_code& ec, std::shared_ptr<asio::ip::tcp::socket> sock){
if(ec.value() == 0){
(new Service(sock))->StartHandling();
}else{
std::cout << "Error occurred! Error code = " << ec.value() << ". Message: " << ec.message();
}
// Init next accept operation if acceptor has not been stopped yet
if(!m_isStopped.load()){
InitAccept();
}else{
// free resources
m_acceptor.close();
}
}
private:
asio::io_service& m_ios;
asio::ip::tcp::acceptor m_acceptor;
std::atomic<bool> m_isStopped;
};
class Server{
public:
Server() {
m_work.reset(new asio::io_service::work(m_ios));
}
// Start the server
void Start(unsigned short port_num, unsigned int thread_pool_size){
assert(thread_pool_size > 0);
// Create and start Acceptor
acc.reset(new Acceptor(m_ios, port_num));
acc->Start();
// Create specified number of thread and add them to the pool
for(unsigned int i = 0; i < thread_pool_size; i++){
std::cout << "Thread " << i << " Running !";
std::unique_ptr<std::thread> th(new std::thread([this](){
m_ios.run();
}));
m_thread_pool.push_back(std::move(th));
}
}
// Stop the Server
void Stop(){
acc->Stop();
m_ios.stop();
for(auto& th : m_thread_pool){
th->join();
}
}
private:
asio::io_service m_ios;
std::unique_ptr<asio::io_service::work> m_work;
std::unique_ptr<Acceptor> acc;
std::vector<std::unique_ptr<std::thread>> m_thread_pool;
};
const unsigned int DEFAULT_THREAD_POOL_SIZE = 2;
int main(){
unsigned short port_num = 3333;
try{
Server srv;
unsigned int thread_pool_size = std::thread::hardware_concurrency() * 2;
if (thread_pool_size == 0){
thread_pool_size = DEFAULT_THREAD_POOL_SIZE;
}
srv.Start(port_num, thread_pool_size);
std::this_thread::sleep_for(std::chrono::seconds(60));
srv.Stop();
}
catch(system::system_error &e){
std::cout << "Error occurred! Error code = " << e.code() << ". Message: " << e.what();
}
return 0;
}
The server closes the connection after sending the (empty) response. That leads to EOF on the client, naturally. Just handle it.
There's loads of code smells
delete this; is an abomination, just make Service shared_from_this.
No need to use shared_ptrs other than that
When you use smart pointers, use them. Don't "convert to raw pointer" just to dereference (so *m_socket instead of *m_socket.get()).
In fact, there should be no need to use new, delete or get() in your code
You are accessing the m_request immediately after async_read_until which is too early,
it is a data race (so Undefined Behaviour)
it doesn't get the request, because async_read_until didn't complete yet.
So move that code into onRequestReceived at a minimum
It's pretty unnecessary to use an istream to read the line from the request when you already have bytes_transferred. I'd suggest
if (bytes_transferred) {
std::string line(m_request.data().data(), bytes_transferred - 1);
m_request.consume(bytes_transferred);
std::cout << "request: " << line << std::endl;
}
Or even:
std::cout << "request: ";
std::cout.write(asio::buffer_cast<char const*>(m_request.data()),
bytes_transferred - 1);
m_request.consume(bytes_transferred);
Or, if you indeed wanted to show the entire m_request, simply
std::cout << "m_request: " << &m_request << std::endl;
Note that read_until may read more than just including the delimiter; for your safety you might want to validate that no other data is trailing, or process it as well
Never switch on error_code::value(), that loses the error category, which is essential to interpret error codes.
Why unique_ptr for each thread? Just a deque<thread>:
while (thread_pool_size--)
m_thread_pool.emplace_back([this] { m_ios.run(); });
But see Should the exception thrown by boost::asio::io_service::run() be caught?
Why unique_ptr for acceptor?
Why a separate class for acceptor? It's not like the server allows more than 1
why a vector of threads anyways? Prefer boost::thread_group
why a manual thread pool? Prefer asio::thread_pool - which already uses the hardware_concurrency if available
In terms of review, the TCPAsyncClient looks like an attempt to implement async_result protocol. It misses the mark on many points. So I'll just point to something like how do i return the response back to caller asynchronously using a final callback dispatched from on_read handler? or How do I make this HTTPS connection persistent in Beast?. They have pretty similar interfaces (perhaps except for the cancellation, if I remember correctly).
Fixed/Return Demo
Here's the completed sample. It includes request parsing, so the server waits the actual amount of time requested.
I scaled all the times down 10x so it can complete online.
Client and server are in single source. Starting with:
./sotest&
./sotest client
wait
Completes both in 6 seconds (see screengrab below)
Live On Coliru
#include <boost/asio.hpp>
#include <boost/spirit/home/x3.hpp> // for request parsing
#include <iomanip>
#include <iostream>
#include <map>
#include <mutex>
#include <thread>
namespace asio = boost::asio;
using asio::ip::tcp;
using boost::system::error_code;
using namespace std::chrono_literals;
using std::this_thread::sleep_for;
/////// server //////////////////////////////////////////////////////////
struct Service : std::enable_shared_from_this<Service> {
Service(tcp::socket sock) : m_sock(std::move(sock)) {}
void StartHandling() {
async_read_until(
m_sock, asio::dynamic_buffer(m_request), '\n',
[this, self = shared_from_this()](error_code ec, size_t bytes) {
onRequestReceived(ec, bytes);
});
}
private:
void onRequestReceived(error_code ec, size_t /*bytes*/) {
std::cout << "onRequestReceived: " << ec.message() << std::endl;
if (ec)
return;
// Process the request
m_response = ProcessingRequest(m_request);
async_write(
m_sock, asio::buffer(m_response),
[this, self = shared_from_this()](error_code ec, size_t bytes) {
onResponseSent(ec, bytes);
});
}
void onResponseSent(error_code ec, size_t /*bytes*/) {
std::cout << "onResponseSent: " << ec.message() << std::endl;
}
std::string static ProcessingRequest(std::string request) {
std::cout << "request: " << request << std::endl;
// parse the request, process it and prepare the response
namespace x3 = boost::spirit::x3;
double value;
if (parse(request.begin(), request.end(),
"EMULATE_LONG_CALC_OP " >> x3::double_ >> "s" >> x3::eol >> x3::eoi,
value)) //
{
// Emulating time-consuming operation
sleep_for(1.0s * value);
return "Waited " + std::to_string(value) + "s\n";
}
return "Unknown request\n";
}
tcp::socket m_sock;
std::string m_request, m_response;
};
struct Server {
Server(asio::any_io_executor ex, uint16_t port_num)
: m_acceptor{ex, {{}, port_num}} {
m_acceptor.listen();
accept_loop();
}
void Stop() { m_acceptor.cancel(); }
private:
void accept_loop() {
m_acceptor.async_accept([this](error_code ec, tcp::socket sock) {
std::cout << "OnAccept: " << ec.message() << std::endl;
if (!ec) {
std::make_shared<Service>(std::move(sock))->StartHandling();
accept_loop();
}
//m_acceptor.close();
});
}
tcp::acceptor m_acceptor;
};
void server(uint16_t port) try {
asio::thread_pool io;
Server srv{io.get_executor(), port};
sleep_for(6s);
srv.Stop();
io.join();
} catch (std::exception const& e) {
std::cout << "Exception: " << e.what() << std::endl;
}
/////// client //////////////////////////////////////////////////////////
struct RequestOp : public std::enable_shared_from_this<RequestOp> {
using Callback = std::function<void( //
unsigned /*request_id*/, std::string_view /*response*/, error_code)>;
RequestOp(asio::any_io_executor ex, const std::string& raw_ip_address,
uint16_t port_num, std::string request, unsigned id,
Callback callback)
: m_ep(asio::ip::address::from_string(raw_ip_address), port_num)
, m_sock(ex, m_ep.protocol())
, m_request(std::move(request))
, m_id(id)
, m_callback(callback) {}
void Run() {
// assumed on logical strand
m_sock.async_connect(
m_ep, [this, self = shared_from_this()](error_code ec) {
if ((m_ec = ec) || m_was_cancelled)
return onComplete();
asio::async_write(m_sock, asio::buffer(m_request),
[this, self = shared_from_this()](
error_code ec, size_t /*bytes*/) {
onRequestWritten(ec);
});
});
}
void Cancel() {
m_was_cancelled = true;
dispatch(m_sock.get_executor(), [self=shared_from_this()]{ self->doCancel(); });
}
private:
void doCancel() {
m_sock.cancel();
}
void onRequestWritten(error_code ec) {
if ((m_ec = ec) || m_was_cancelled)
return onComplete();
asio::async_read_until(
m_sock, asio::dynamic_buffer(m_response), '\n',
[this, self = shared_from_this()](error_code ec, size_t bytes) {
onResponseReceived(ec, bytes);
});
}
void onResponseReceived(error_code ec, size_t /*bytes*/) {
if ((m_ec = ec) || m_was_cancelled)
return onComplete();
if (!m_response.empty())
m_response.resize(m_response.size() - 1); // drop '\n'
onComplete();
}
void onComplete() {
// shutting down the connection, we don't care about the error code
// if function failed
error_code ignored_ec;
m_sock.shutdown(tcp::socket::shutdown_both, ignored_ec);
if(!m_ec && m_was_cancelled){
m_ec = asio::error::operation_aborted;
}
m_callback(m_id, m_response, m_ec);
}
tcp::endpoint m_ep; // Remote endpoint
tcp::socket m_sock;
std::string m_request;
std::string m_response; // Response represented as a string
error_code m_ec;
unsigned m_id;
Callback m_callback;
std::atomic_bool m_was_cancelled{false};
};
class AsyncTCPClient {
public:
AsyncTCPClient(asio::any_io_executor ex) : m_executor(ex) {}
using Duration = std::chrono::steady_clock::duration;
size_t emulateLongCalcOp(Duration delay, std::string const& raw_ip_address,
uint16_t port_num, RequestOp::Callback callback) {
auto request =
"EMULATE_LONG_CALC_OP " + std::to_string(delay / 1.0s) + "s\n";
std::cout << "Request: " << request << std::flush;
auto const request_id = m_nextId++;
auto session = std::make_shared<RequestOp>(
make_strand(m_executor), //
raw_ip_address, port_num, request, request_id, callback);
{
// active sessions list can be accessed from multiple thread, we
// guard it with a mutex to avoid data coruption
std::unique_lock lock(m_active_sessions_guard);
auto [_,ok] = m_pending_ops.emplace(request_id, session);
assert(ok); // duplicate request_id?
// optionally: garbage collect completed sessions
std::erase_if(m_pending_ops,
[](auto& kv) { return kv.second.expired(); });
};
session->Run();
return request_id;
}
// Cancels the request
void cancelRequest(unsigned request_id) {
std::unique_lock lock(m_active_sessions_guard);
if (auto session = m_pending_ops[request_id].lock())
session->Cancel();
}
private:
using PendingOp = std::weak_ptr<RequestOp>;
asio::any_io_executor m_executor;
std::mutex m_active_sessions_guard;
size_t m_nextId = 1;
std::map<int, PendingOp> m_pending_ops;
};
void handler(unsigned request_id, std::string_view response, error_code ec) {
std::cout << "Request #" << request_id << " ";
if (!ec.failed())
std::cout << "Response: " << std::quoted(response) << std::endl;
else if (ec == asio::error::operation_aborted)
std::cout << "Cancelled" << std::endl;
else
std::cout << ec.message() << std::endl;
}
void client(uint16_t port) try {
asio::thread_pool io;
{
AsyncTCPClient client(io.get_executor());
auto id1 = client.emulateLongCalcOp(4s, "127.0.0.1", port, handler);
auto id2 = client.emulateLongCalcOp(1100ms, "127.0.0.1", port, handler);
auto id3 = client.emulateLongCalcOp(3500ms, "127.0.0.1", port, handler);
// cancel request 1
sleep_for(3s);
client.cancelRequest(id1);
sleep_for(1200ms);
client.cancelRequest(id2); // no effect, already completed
client.cancelRequest(id3); // no effect, already completed
// exit the application
}
io.join();
} catch (std::exception const& e) {
std::cout << "Exception: " << e.what() << std::endl;
}
/////// main //////////////////////////////////////////////////////////
int main(int argc, char**) {
if (argc > 1)
client(3333);
else
server(3333);
}
Prints client:
Request: EMULATE_LONG_CALC_OP 4.000000s
Request: EMULATE_LONG_CALC_OP 1.100000s
Request: EMULATE_LONG_CALC_OP 3.500000s
Request #2 Response: "Waited 1.100000s"
Request #1 Cancelled
Request #3 Response: "Waited 3.500000s"
Prints server:
OnAccept: Success
OnAccept: Success
onRequestReceived: Success
request: EMULATE_LONG_CALC_OP 1.100000s
onRequestReceived: Success
request: EMULATE_LONG_CALC_OP 4.000000s
OnAccept: Success
onRequestReceived: Success
request: EMULATE_LONG_CALC_OP 3.500000s
onResponseSent: Success
onResponseSent: Success
onResponseSent: Success
OnAccept: Operation canceled

Asynchronous server shuts down immediately after creating boost::asio

The server starts and accepts connections, all clients, even if more than 10 are connected, send a message but there is no response.
The read and write function uses the index of the received client's account and works with it. Therefore, there is an additional parameter in the headers.
We accept the connection and pass its number to the header and there with the socket of this number we are working.
#include <iostream>
#include <boost/asio.hpp>
#include <boost/thread.hpp>
#include <clocale>
#include <vector>
#include <conio.h>
using namespace boost::asio;
using namespace std;
class tcp_server
{
private:
io_service service;
int port;
enum { buff_size = 1024 };
ip::tcp::endpoint endpoint;
ip::tcp::acceptor acceptor;
int countClients = 0;
int accept_i = 0;
struct client
{
ip::tcp::socket sock;
char buff[buff_size] = { };
};
vector<client> clients;
public:
tcp_server(io_service& service, int port) : service(), acceptor(service), endpoint(ip::tcp::v4(), port)
{
this->port;
acceptor.open(endpoint.protocol());
acceptor.set_option(ip::tcp::acceptor::reuse_address(true));
acceptor.bind(endpoint);
acceptor.listen();
clients.reserve(10);
}
void start()
{
start_service_in_thread();
}
void start_service_in_thread()
{
for (int i = 0; i < 10; ++i)
boost::thread(service_func_for_thread);
for (int i = 0; i < 10; ++i)
{
boost::thread(acceptor_func_for_thread);
accept_i++;
}
}
void service_func_for_thread()
{
service.run();
}
void accept_handler(const boost::system::error_code& error)
{
if (!error)
{
countClients++;
do_read_this(countClients - 1);
}
else
{
cout << "Acceptor error\n";
cout << error.message() << endl;
}
}
void acceptor_func_for_thread()
{
acceptor.async_accept(
clients[accept_i].sock,
boost::bind(&tcp_server::accept_handler, this, boost::asio::placeholders::error)
);
}
void do_read_this(int thisClientIndex)
{
clients[thisClientIndex].sock.async_read_some(
buffer(clients[thisClientIndex].buff),
boost::bind(&tcp_server::read_handler,
this,
boost::asio::placeholders::error,
boost::asio::placeholders::bytes_transferred,
thisClientIndex)
);
}
void read_handler(const boost::system::error_code& error, size_t bytes_transferred, int thisClientIndex)
{
if (!error)
{
clients[thisClientIndex].sock.async_write_some(
buffer(clients[thisClientIndex].buff),
boost::bind(&tcp_server::write_handler,
this,
boost::asio::placeholders::error,
boost::asio::placeholders::bytes_transferred,
thisClientIndex)
);
}
else
{
cout << "Error reading from socket\n";
cout << error.message() << endl;
}
}
void write_handler(const boost::system::error_code& error, size_t bytes_transferred, int thisClientIndex)
{
if (!error)
{
do_read_this(thisClientIndex);
}
else
{
cout << "Error write in socket\n";
cout << error.message() << endl;
}
}
};
int main(int argc, char *argv[])
{
try
{
setlocale(LC_ALL, "Rus");
io_service service;
tcp_server* server = new tcp_server{ service, 5000 };
server->start();
service.run();
}
catch (exception& ex)
{
cout << "Exception: " << ex.what();
}
return 0;
}
The client connects to the server and when it sends a connection, no response is received.
Please help.
service.run(); in main has nothing to do so it returns immediately so main returns causing program to end.
Creating background threads is not necessary here.
You are (again) creating a temporary objects boost::thread that immediately go out of scope. And unless BOOST_THREAD_PROVIDES_THREAD_DESTRUCTOR_CALLS_TERMINATE_IF_JOINABLE is specified you will end up with a bunch of detached threads.
When the io_service::run() method has no work to do, it returns.
You should either
post() at least one task to the io_service before calling run(),
or "lock" it with io_service::work
io_service service;
boost::asio::io_service::work work(service);
The latter requires a call to service.stop() to cause run() to exit, otherwise it will run eternally.
Note however: you don't really need two io_services or any threads in an async application.

Blocking signals causes boost process not to work

In the code below the class Process can run a process using boost process in asynchronous mode and can kill it if it times out. Now in order to shut it down, I block all the signals in all threads and create a specific thread signal_thread to handle signals. On doing this the program stops working. I guess this is probably because the parent process can no longer receive the signal SIGCHLD and know that the child process has finished executing.
#include <iostream>
#include <csignal>
#include <thread>
#include <chrono>
#include <future>
#include <boost/process.hpp>
#include <boost/asio.hpp>
namespace bp = boost::process;
std::atomic<bool> stop(false);
class Process
{
public:
Process(const std::string& cmd, const int timeout);
void run();
private:
void timeoutHandler(const boost::system::error_code& ec);
void kill();
const std::string command;
const int timeout;
bool stopped;
boost::process::group group;
boost::asio::io_context ioc;
boost::asio::deadline_timer deadline_timer;
unsigned returnStatus;
};
Process::Process(const std::string& cmd, const int timeout):
command(cmd),
timeout(timeout),
stopped(false),
ioc(),
deadline_timer(ioc),
returnStatus(0)
{}
void Process::timeoutHandler(const boost::system::error_code& ec)
{
if (stopped || ec == boost::asio::error::operation_aborted)
{
return;
}
std::cout << "Time Up!" << std::endl;
kill();
deadline_timer.expires_at(boost::posix_time::pos_infin);
}
void Process::run()
{
std::future<std::string> dataOut;
std::future<std::string> dataErr;
std::cout << "Running command: "<< command << std::endl;
bp::child c(command, bp::std_in.close(),
bp::std_out > dataOut,
bp::std_err > dataErr, ioc,
group,
bp::on_exit([=](int e, const std::error_code& ec) {
std::cout << "on_exit: " << ec.message() << " -> "<< e << std::endl;
deadline_timer.cancel();
returnStatus = e;
}));
deadline_timer.expires_from_now(boost::posix_time::seconds(timeout));
deadline_timer.async_wait(std::bind(&Process::timeoutHandler, this, std::placeholders::_1));
ioc.run();
c.wait();
std::cout << "returnStatus "<< returnStatus << std::endl;
std::cout << "stdOut "<< dataOut.get() << std::endl;
std::cout << "stdErr "<< dataErr.get() << std::endl;
}
void Process::kill()
{
std::error_code ec;
group.terminate(ec);
if(ec)
{
std::cerr << "Error occurred while trying to kill the process: " << ec.message() << std::endl;
throw std::runtime_error(ec.message());
}
std::cout << "Killed the process and all its descendants" << std::endl;
stopped = true;
}
void myfunction()
{
while(true)
{
Process p("date", 3600);
p.run();
std::this_thread::sleep_for(std::chrono::milliseconds(3000));
if(stop)
break;
}
}
int main() {
sigset_t sigset;
sigfillset(&sigset);
::pthread_sigmask(SIG_BLOCK, &sigset, nullptr);
std::thread signal_thread([]() {
while(true)
{
sigset_t sigset;
sigfillset(&sigset);
int signo = ::sigwaitinfo(&sigset, nullptr);
if(-1 == signo)
std::abort();
std::cout << "Received signal " << signo << '\n';
if(signo != SIGCHLD)
{
break;
}
}
stop = true;
});
myfunction();
signal_thread.join();
}
Please suggest how I can shut down the program using the signal handling thread as well make the program work correctly.
Thinking more about it, I suggest blocking only signals that you intend for that signal thread to handle, such as SIGINT and SIGTERM:
sigset_t sigset;
sigemptyset(&sigset);
sigaddset(&sigset, SIGINT);
sigaddset(&sigset, SIGTERM);
::pthread_sigmask(SIG_BLOCK, &sigset, nullptr);
std::thread signal_thread([sigset]() { // Use the same sigset.
// ...
int signo = ::sigwaitinfo(&sigset, nullptr);
// ...
});

C++ Boost UDP receiver fails when put into thread

I have a UDP receiver that works. The code is here:
#include <array>
#include <iostream>
#include <string>
#include <boost/asio.hpp>
std::string getMyIp()
{
std::string result;
try
{
boost::asio::io_service netService;
boost::asio::ip::udp::resolver resolver(netService);
boost::asio::ip::udp::udp::resolver::query query(boost::asio::ip::udp::v4(), "google.com", "");
boost::asio::ip::udp::udp::resolver::iterator endpoints = resolver.resolve(query);
boost::asio::ip::udp::udp::endpoint ep = *endpoints;
boost::asio::ip::udp::udp::socket socket(netService);
socket.connect(ep);
boost::asio::ip::address addr = socket.local_endpoint().address();
result = addr.to_string();
//std::cout << "My IP according to google is: " << results << std::endl;
}
catch (std::exception& e)
{
std::cerr << "Could not deal with socket. Exception: " << e.what() << std::endl;
}
return result;
}
class receiver
{
private:
boost::asio::ip::udp::socket socket_;
boost::asio::ip::udp::endpoint sender_endpoint_;
std::array<char, 1024> data_;
public:
receiver(boost::asio::io_service& io_service,
const boost::asio::ip::address& listen_address,
const boost::asio::ip::address& multicast_address,
unsigned short multicast_port = 13000)
: socket_(io_service)
{
// Create the socket so that multiple may be bound to the same address.
boost::asio::ip::udp::endpoint listen_endpoint(listen_address, multicast_port);
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(multicast_address));
do_receive();
}
private:
void do_receive()
{
socket_.async_receive_from(boost::asio::buffer(data_), sender_endpoint_, [this](boost::system::error_code ec, std::size_t length)
{
if (!ec)
{
std::cout.write(data_.data(), length);
std::cout << std::endl;
do_receive();
}
});
}
};
int main(int argc, char* argv[])
{
try
{
boost::asio::io_service io_service;
receiver r(io_service, boost::asio::ip::make_address(getMyIp()), boost::asio::ip::make_address("224.0.0.0"), 13000);
io_service.run();
}
catch (std::exception& e)
{
std::cerr << "Exception: " << e.what() << "\n";
}
return 0;
}
I want to put the receiver code into a thread inside a class so I can do other things beside it:
#define _CRT_SECURE_NO_WARNINGS
#include <ctime>
#include <iostream>
#include <string>
#include <queue>
#include <boost/array.hpp>
#include <boost/bind.hpp>
#include <boost/shared_ptr.hpp>
#include <boost/asio.hpp>
#include <boost/lexical_cast.hpp>
#include <boost/thread.hpp>
#include <boost/thread/thread.hpp>
#include <boost/chrono.hpp>
#include <boost/date_time/posix_time/posix_time.hpp>
using boost::asio::ip::udp;
using std::cout;
using std::cin;
using std::endl;
using std::string;
using namespace std;
std::string getMyIp()
{
std::string result;
try
{
boost::asio::io_service netService;
boost::asio::ip::udp::resolver resolver(netService);
boost::asio::ip::udp::udp::resolver::query query(boost::asio::ip::udp::v4(), "google.com", "");
boost::asio::ip::udp::udp::resolver::iterator endpoints = resolver.resolve(query);
boost::asio::ip::udp::udp::endpoint ep = *endpoints;
boost::asio::ip::udp::udp::socket socket(netService);
socket.connect(ep);
boost::asio::ip::address addr = socket.local_endpoint().address();
result = addr.to_string();
//std::cout << "My IP according to google is: " << results << std::endl;
}
catch (std::exception& e)
{
std::cerr << "Could not deal with socket. Exception: " << e.what() << std::endl;
}
return result;
}
class UdpReceiver
{
private:
boost::asio::ip::udp::socket socket_;
boost::asio::ip::udp::endpoint sender_endpoint_;
std::array<char, 1024> data_;
string address_send, address_recv;
unsigned short port_send, port_recv;
boost::thread_group threads; // thread group
boost::thread* thread_main; // main thread
boost::thread* thread_receive; // receive thread
boost::thread* thread_send; // get/send thread
boost::mutex stopMutex;
bool initialize = false;
bool stop, showBroadcast;
int i_send, i_recv, i_operator,
interval_send, interval_recv, interval_operator,
mode;
string message_send, message_recv;
string message_STOP = "STOP";
public:
// constructor
UdpReceiver(boost::asio::io_service& io_service, std::string address, unsigned short port, int interval, int mode, bool show = false)
: socket_(io_service),
showBroadcast(show)
{
initialize = false;
Initialize(io_service, show);
}
UdpReceiver(boost::asio::io_service& io_service, bool show = false)
: socket_(io_service),
showBroadcast(show)
{
Initialize(io_service, show);
}
// destructor
~UdpReceiver()
{
// show exit message
cout << "Exiting UDP Core." << endl;
}
// initialize
void Initialize(boost::asio::io_service& io_service, bool show = false)
{
if (initialize == false)
{
GetMode(true);
GetInfo(true);
}
CreateEndpoint(io_service);
CreateThreads();
stop = false;
showBroadcast = show;
i_send = 0;
i_recv = 0;
i_operator = 0;
message_send.clear();
message_recv.clear();
initialize = true; // clear flag
}
void GetMode(bool default_value = false)
{
std::string input;
if (default_value)
{
mode = 0;
}
else
{
string prompt = "Set mode:\n0/other - Listen\n1 - Send\nEnter your choice: ";
cout << prompt;
getline(cin, input);
try
{
mode = stoi(input);
// set default mode to Listen
if (mode > 1)
mode = 0;
}
catch (exception ec)
{
cout << "Error converting mode: " << ec.what() << endl;
Stop();
}
}
}
void GetInfo(bool default_value = false)
{
// always called after GetMode()
string address;
unsigned short port;
int interval;
if (default_value)
{
address = getMyIp();
port = 13000;
interval = 500;
}
switch (mode)
{
case 0:
address_recv = address;
port_recv = port;
interval_recv = interval;
break;
case 1:
address_send = address;
port_send = port;
interval_send = interval;
break;
default:
// already set to 0 in GetMode()
break;
}
}
void CreateEndpoint(boost::asio::io_service& io_service)
{
// 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(address_recv), port_recv);
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("224.0.0.0")));
}
void CreateThreads()
{
thread_main = new boost::thread(boost::ref(*this));
interval_operator = 500; // default value
switch (mode)
{
case 0:
thread_receive = new boost::thread(&UdpReceiver::Callable_Receive, this);
threads.add_thread(thread_receive);
break;
default:
// already set to 0 in GetMode()
break;
}
}
// start the threads
void Start()
{
// Wait till they are finished
threads.join_all();
}
// stop the threads
void Stop()
{
// warning message
cout << "Stopping all threads." << endl;
// signal the threads to stop (thread-safe)
stopMutex.lock();
stop = true;
stopMutex.unlock();
// wait for the threads to finish
thread_main->interrupt(); // in case not interrupted by operator()
threads.interrupt_all();
threads.join_all();
// close socket after everything closes
//socketPtr->close();
socket_.close();
}
void Callable_Receive()
{
while (!stop)
{
stopMutex.lock();
socket_.async_receive_from(boost::asio::buffer(data_), sender_endpoint_, [this](boost::system::error_code ec, std::size_t length)
{
if (!ec)
{
//cout << message_recv << endl;
std::cout.write(data_.data(), length);
std::cout << std::endl;
Callable_Receive();
}
});
stopMutex.unlock();
//cout << i_recv << endl;
++i_recv;
}
}
// Thread function
void operator () ()
{
while (!stop)
{
if (message_send == message_STOP)
{
try
{
this->Stop();
}
catch (exception e)
{
cout << e.what() << endl;
}
}
boost::this_thread::sleep(boost::posix_time::millisec(interval_operator));
boost::this_thread::interruption_point();
}
}
};
int main()
{
try
{
boost::asio::io_service io_service;
UdpReceiver mt(io_service, false);
mt.Start();
}
catch (std::exception& e)
{
std::cerr << "Exception: " << e.what() << "\n";
}
}
The async receive is inside Callable_Receive(), which is inside by thread_receive. I can see that thread running when the counter is printed on screen (which I comment out). However, the async_receive_from() never receives anything. Could someone tell me why this happens?
You have probably deadlock in Callable_Receive. In thread with Callable_Receive as body of thread you are calling stopMutex.lock before invoking async_receive_from function. async_receive_from returns immediately, but we don't know when lambda object passed as third paremeter to async_receive_from will be called. When body of lambda object is executed, you are calling Callable_Receive function, if stopMutex was locked (thread with Callable_Receive is still running and next iteration in while loop is being done) and you try to lock it again, you would get deadlock - on boost::mutex you cannot call lock method while mutex is already being locked by the same thread.
You should read about boost::recursive_mutex if you want to resolve this issue.

boost asio deadline_timer async_wait(N seconds) twice within N seconds cause operation canceled

What I want is when one message queue receives an int N, the handler function will be called after N seconds. below is my code.
It runs OK if the duration seconds of two near message queue is larger than the int N, but the handler will print "Operation canceled" in one handler when the duration seconds between two received message queues are smaller than N, which is not what I want.
I'd appreciate a lot for any help.
#include <boost/asio.hpp>
#include <zmq.h>
#include <boost/thread.hpp>
#include <iostream>
boost::asio::io_service io_service;
void* context = zmq_ctx_new();
void* sock_pull = zmq_socket(context, ZMQ_PULL);
void handler(const boost::system::error_code &ec) {
std::cout << "hello, world" << "\t" << ec.message() << std::endl;
}
void run() {
io_service.run();
}
void thread_listener() {
int nRecv;
boost::asio::deadline_timer timer(io_service, boost::posix_time::seconds(0));
while( true ) {
zmq_recv(sock_pull, &nRecv, sizeof(nRecv), 0);
std::cout << nRecv << std::endl;
timer.expires_from_now(boost::posix_time::seconds(nRecv));
timer.async_wait(handler);
}
}
int main(int argc, char* argv[]) {
boost::asio::io_service::work work(io_service);
zmq_bind(sock_pull, "tcp://*:60000");
boost::thread tThread(thread_listener);
boost::thread tThreadRun(run);
tThread.join();
tThreadRun.join();
return 0;
}
When you call
timer.expires_from_now(boost::posix_time::seconds(nRecv));
this, as the documentation states, cancels any async timer pending.
If you want to have overlapping requests in flight at a given time, one timer is clearly not enough. Luckily there is a wellknown pattern around bound shared pointers in Asio that you can use to mimick a "session" per response.
Say you define a session to contain it's own private timer:
struct session : boost::enable_shared_from_this<session> {
session(boost::asio::io_service& svc, int N) :
timer(svc, boost::posix_time::seconds(N))
{
// Note: shared_from_this is not allowed from ctor
}
void start() {
// it's critical that the completion handler is bound to a shared
// pointer so the handler keeps the session alive:
timer.async_wait(boost::bind(&session::handler, shared_from_this(), boost::asio::placeholders::error));
}
private:
void handler(const boost::system::error_code &ec) {
std::cout << "hello, world" << "\t" << ec.message() << std::endl;
}
boost::asio::deadline_timer timer;
};
Now, it's trivial to replace the code that used the hardcoded timer instance:
timer.expires_from_now(boost::posix_time::seconds(nRecv));
timer.async_wait(handler);
with the session start:
boost::make_shared<session>(io_service, nRecv)->start();
A fully working example (with suitably stubbed ZMQ stuff): Live On Coliru
#include <boost/asio.hpp>
#include <boost/thread.hpp>
#include <boost/enable_shared_from_this.hpp>
#include <boost/make_shared.hpp>
#include <iostream>
boost::asio::io_service io_service;
/////////////////////////////////////////////////////////////////////////
// I love stubbing out stuff I don't want to install just to help others
enum { ZMQ_PULL };
static void* zmq_ctx_new() { return nullptr; }
static void* zmq_socket(void*,int) { return nullptr; }
static void zmq_bind(void*,char const*) {}
static void zmq_recv(void*,int*data,size_t,int)
{
boost::this_thread::sleep_for(boost::chrono::milliseconds(rand()%1000));
*data = 2;
}
// End of stubs :)
/////////////////////////////////////////////////////////////////////////
void* context = zmq_ctx_new();
void* sock_pull = zmq_socket(context, ZMQ_PULL);
struct session : boost::enable_shared_from_this<session> {
session(boost::asio::io_service& svc, int N) :
timer(svc, boost::posix_time::seconds(N))
{
// Note: shared_from_this is not allowed from ctor
}
void start() {
// it's critical that the completion handler is bound to a shared
// pointer so the handler keeps the session alive:
timer.async_wait(boost::bind(&session::handler, shared_from_this(), boost::asio::placeholders::error));
}
~session() {
std::cout << "bye (session end)\n";
}
private:
void handler(const boost::system::error_code &ec) {
std::cout << "hello, world" << "\t" << ec.message() << std::endl;
}
boost::asio::deadline_timer timer;
};
void run() {
io_service.run();
}
void thread_listener() {
int nRecv = 0;
for(int n=0; n<4; ++n) {
zmq_recv(sock_pull, &nRecv, sizeof(nRecv), 0);
std::cout << nRecv << std::endl;
boost::make_shared<session>(io_service, nRecv)->start();
}
}
int main() {
auto work = boost::make_shared<boost::asio::io_service::work>(io_service);
zmq_bind(sock_pull, "tcp://*:60000");
boost::thread tThread(thread_listener);
boost::thread tThreadRun(run);
tThread.join();
work.reset();
tThreadRun.join();
}