C++ Paho MQTT connection as a subscriber issue - c++

I'm pretty new to C++ and was playing around with the Paho MQTT C++ client.
I've rearranged the sample async subscriber, but ran into an error of 'connection lost, cause: connect onSuccess called'. Spent hours trying to find the solution to no prevail.
My Main file:
int main()
{
subscriber();
return 0;
}
My header file: 'subscriber.hpp' ..has all the dependacy and mqtt/async_client.h
int subscriber();
My source file: subscriber.cpp
#include "subscriber.hpp"
const std::string SERVER_ADDRESS("tcp://localhost:1883");
const std::string CLIENT_ID("eclipse_subscriber");
const std::string TOPIC("topic2");
const int QOS = 1;
const int N_RETRY_ATTEMPTS = 5;
/////////////////////////////////////////////////////////////////////////////
// Callbacks for the success or failures of requested actions.
// This could be used to initiate further action, but here we just log the
// results to the console.
class action_listener : public virtual mqtt::iaction_listener
{
std::string name_;
void on_failure(const mqtt::token& tok) override {
std::cout << name_ << " failure";
if (tok.get_message_id() != 0)
std::cout << " for token: [" << tok.get_message_id() << "]" << std::endl;
std::cout << std::endl;
}
void on_success(const mqtt::token& tok) override {
std::cout << name_ << " success";
if (tok.get_message_id() != 0)
std::cout << " for token: [" << tok.get_message_id() << "]" << std::endl;
auto top = tok.get_topics();
if (top && !top->empty())
std::cout << "\ttoken topic: '" << (*top)[0] << "', ..." << std::endl;
std::cout << std::endl;
}
public:
action_listener(const std::string& name) : name_(name) {}
};
/////////////////////////////////////////////////////////////////////////////
/**
* Local callback & listener class for use with the client connection.
* This is primarily intended to receive messages, but it will also monitor
* the connection to the broker. If the connection is lost, it will attempt
* to restore the connection and re-subscribe to the topic.
*/
class callback : public virtual mqtt::callback,
public virtual mqtt::iaction_listener
{
// Counter for the number of connection retries
int nretry_;
// The MQTT client
mqtt::async_client& cli_;
// Options to use if we need to reconnect
mqtt::connect_options& connOpts_;
// An action listener to display the result of actions.
action_listener subListener_;
// This deomonstrates manually reconnecting to the broker by calling
// connect() again. This is a possibility for an application that keeps
// a copy of it's original connect_options, or if the app wants to
// reconnect with different options.
// Another way this can be done manually, if using the same options, is
// to just call the async_client::reconnect() method.
void reconnect() {
std::this_thread::sleep_for(std::chrono::milliseconds(2500));
try {
cli_.connect(connOpts_, nullptr, *this);
}
catch (const mqtt::exception& exc) {
std::cerr << "Error: " << exc.what() << std::endl;
exit(1);
}
}
// Re-connection failure
void on_failure(const mqtt::token& tok) override {
std::cout << "Connection attempt failed" << std::endl;
if (++nretry_ > N_RETRY_ATTEMPTS)
exit(1);
reconnect();
}
// (Re)connection success
// Either this or connected() can be used for callbacks.
void on_success(const mqtt::token& tok) override {}
// (Re)connection success
void connected(const std::string& cause) override {
std::cout << "\nConnection success" << std::endl;
std::cout << "\nSubscribing to topic '" << TOPIC << "'\n"
<< "\tfor client " << CLIENT_ID
<< " using QoS" << QOS << "\n"
<< "\nPress Q<Enter> to quit\n" << std::endl;
cli_.subscribe(TOPIC, QOS, nullptr, subListener_);
}
// Callback for when the connection is lost.
// This will initiate the attempt to manually reconnect.
void connection_lost(const std::string& cause) override {
std::cout << "\nConnection lost" << std::endl;
if (!cause.empty())
std::cout << "\tcause: " << cause << std::endl;
std::cout << "Reconnecting..." << std::endl;
nretry_ = 0;
reconnect();
}
// Callback for when a message arrives.
void message_arrived(mqtt::const_message_ptr msg) override {
std::cout << "Message arrived" << std::endl;
std::cout << "\ttopic: '" << msg->get_topic() << "'" << std::endl;
std::cout << "\tpayload: '" << msg->to_string() << "'\n" << std::endl;
}
void delivery_complete(mqtt::delivery_token_ptr token) override {}
public:
callback(mqtt::async_client& cli, mqtt::connect_options& connOpts)
: nretry_(0), cli_(cli), connOpts_(connOpts), subListener_("Subscription") {}
};
/////////////////////////////////////////////////////////////////////////////
int subscriber()
{
mqtt::connect_options connOpts;
connOpts.set_keep_alive_interval(20);
connOpts.set_clean_session(true);
mqtt::async_client client(SERVER_ADDRESS, CLIENT_ID);
callback cb(client, connOpts);
client.set_callback(cb);
// Start the connection.
// When completed, the callback will subscribe to topic.
try {
std::cout << "Connecting to the MQTT server..." << std::flush;
client.connect(connOpts, nullptr, cb);
}
catch (const mqtt::exception&) {
std::cerr << "\nERROR: Unable to connect to MQTT server: '"
<< SERVER_ADDRESS << "'" << std::endl;
return 1;
}
// Just block till user tells us to quit.
while (std::tolower(std::cin.get()) != 'q')
;
// Disconnect
try {
std::cout << "\nDisconnecting from the MQTT server..." << std::flush;
client.disconnect()->wait();
std::cout << "OK" << std::endl;
}
catch (const mqtt::exception& exc) {
std::cerr << exc.what() << std::endl;
return 1;
}
return 0;
}

Related

How do I make async_read_some of asio to call a callback?

I'm trying to use serial_port of asio(standlone) to get data from a device. But I can't read even a byte.
I post a screenshoot of the example usage found from documentation website as below :
enter image description here
enter image description here
https://think-async.com/Asio/asio-1.24.0/doc/asio/reference/basic_serial_port/async_read_some.html
There are 3 question I have:
What is the specific type of the first parameter of async_read_some? std::vector<uint8_t>, for example?
How to bind a class member function as the callback/handler?
Is it right that we just make sure io_context.run() run on a background thread and call async_read_some just once?
I'll appreciate it if you guys could help me check out my code or give some advice.
class NmeaSource {
public:
explicit NmeaSource(std::shared_ptr<asio::io_context> io, const nlohmann::json& settings);
void parse();
void handle(const asio::error_code& error, // Result of operation.
std::size_t bytes_transferred // Number of bytes read.
);
private:
void open();
std::string m_port;
int m_baudrate;
std::shared_ptr<asio::io_context> m_io;
std::shared_ptr<asio::serial_port> m_serial;
unsigned char m_readBuffer[1024];
};
NmeaSource::NmeaSource(std::shared_ptr<asio::io_context> io, const nlohmann::json& settings)
:m_io(io)
{
try {
setPort(settings.at("port"));
setBaudrate(settings.at("baudrate"));
//LOG("");
std::cout << "port: " << m_port << ", baudrate: " << m_baudrate << std::endl;
}
catch (nlohmann::json::basic_json::type_error& e1) {
std::cout << "type is wrong " << e1.what() << std::endl;
throw e1;
}
catch (nlohmann::json::basic_json::out_of_range& e2) {
std::cout << "key is not found " << e2.what() << std::endl;
throw e2;
}
catch (...)
{
std::cout << "unknown error"<< std::endl;
exit(-1);
}
open();
}
void NmeaSource::open()
{
m_serial = std::make_shared<asio::serial_port>(*m_io);
asio::error_code ec;
m_serial->open(m_port, ec);
if (!ec) {
asio::serial_port_base::baud_rate baudrate(m_baudrate);
m_serial->set_option(baudrate);
std::cout << "successfully" << std::endl;
}
else {
std::cout << "failed " << ec.message() <<std::endl;
}
}
void NmeaSource::handle(const asio::error_code& error, // Result of operation.
std::size_t bytes_transferred // Number of bytes read.
)
{
if (!error) {
std::cout << bytes_transferred << " bytes read!" << std::endl;
}
else {
std::cout << error.message() << std::endl;
}
}
void NmeaSource::parse()
{
m_serial->async_read_some(
asio::buffer(m_readBuffer, 1024),
std::bind(&NmeaSource::handle, this,
std::placeholders::_1,
std::placeholders::_2)
);
}
int main()
{
auto io = std::make_shared<asio::io_context>();
//std::thread t(std::bind(static_cast<size_t(asio::io_service::*)()>(&asio::io_service::run), io.get()));
std::thread t([&io]() {io->run(); });
NmeaSource rtk(io, nlohmann::json{ {"port", "COM3"}, {"baudrate", 115200} });
rtk.parse();
for (;;)
{
std::this_thread::sleep_for(std::chrono::milliseconds(5));
}
}
The output

Google Pub/Sub C++ C++ Pub/Sub stops receiving

Using the online documentation and the code in the sample repo, I've implemented PubSub for a single incoming and multiple outgoing. The pull PubSub is an ordering subscription (would much prefer an ordered and deliever once, but that doesn't seem allowed). I have dead letter configured for each PubSub. Repeatably, the code will stop pulling messages. Sometimes in 5 minutes, sometimes after a couple of days. This morning, there were 55k messages waiting to be pulled. No messages in the dead letter. Based on the website, I expected something from the then() handler in the subscription, but there is no error messages. Here is the relevant parts my code.
/* These are based on https://github.com/googleapis/google-cloud-cpp/blob/HEAD/google/cloud/pubsub/samples/samples.cc */
/* https://cloud.google.com/pubsub/docs/handling-failures#dead-letter_topic */
/* https://cloud.google.com/blog/products/application-development/running-cplusplus-apps-with-the-help-of-pubsub-and-gke */
/* https://cloud.google.com/pubsub/docs/samples/pubsub-subscriber-error-listener#code-sample */
#include <google/cloud/pubsub/publisher.h>
#include <google/cloud/storage/client.h>
#include <google/cloud/pubsub/subscriber.h>
#include <google/cloud/pubsub/subscription_admin_client.h>
#include <google/cloud/pubsub/topic_admin_client.h>
#include <nlohmann/json.hpp>
void createTopic(const std::string& topicId, const std::string& dlTopicId, google::cloud::pubsub::TopicAdminClient& client)
{
namespace pubsub = ::google::cloud::pubsub;
[](pubsub::TopicAdminClient client, std::string project_id, std::string topic_id, std::string dl_topic_id)
{
auto topic = client.CreateTopic(pubsub::TopicBuilder(pubsub::Topic(project_id, topic_id)));
// Note that kAlreadyExists is a possible error when the library retries.
if (topic.status().code() == google::cloud::StatusCode::kAlreadyExists)
{
;
}
else if (!topic)
{
throw std::runtime_error(topic.status().message());
}
else
{
LOGn << "The topic " << topic_id << " was successfully created: " << topic->DebugString();
}
auto dl_topic = client.CreateTopic(pubsub::TopicBuilder(pubsub::Topic(project_id, dl_topic_id)));
// Note that kAlreadyExists is a possible error when the library retries.
if (dl_topic.status().code() == google::cloud::StatusCode::kAlreadyExists)
{
;
}
else if (!dl_topic)
{
throw std::runtime_error(dl_topic.status().message());
}
else
{
LOGn << "The deadletter topic " << dl_topic_id << " was successfully created: " << dl_topic->DebugString();
}
}
(client, pubSubProjectId, topicId, dlTopicId);
}
void createOrderingSubscription(const std::string& topicId, const std::string& subscriptionId, const std::string& dlTopicId, const std::string& dlSubscriptionId, google::cloud::pubsub::SubscriptionAdminClient& client)
{
namespace pubsub = ::google::cloud::pubsub;
return [](pubsub::SubscriptionAdminClient client, std::string const& project_id, std::string const& topic_id, std::string const& subscription_id, std::string const& dl_topic_id, std::string const& dl_subscription_id)
{
static const constexpr int DEAD_LETTER_DELIVERY_ATTEMPTS = 5;
auto sub = client.CreateSubscription(
pubsub::Topic(project_id, std::move(topic_id)),
pubsub::Subscription(project_id, subscription_id),
pubsub::SubscriptionBuilder{}
.enable_message_ordering(true)
.set_ack_deadline(std::chrono::seconds(60))
.set_dead_letter_policy(
pubsub::SubscriptionBuilder::MakeDeadLetterPolicy(
pubsub::Topic(project_id, dl_topic_id),
DEAD_LETTER_DELIVERY_ATTEMPTS))
);
if (sub.status().code() == google::cloud::StatusCode::kAlreadyExists)
{
;
}
else if (!sub)
{
throw std::runtime_error(sub.status().message());
}
else
{
LOGd << "The subscription " << subscription_id << " was successfully created: " << sub->DebugString();
}
auto dl_sub = client.CreateSubscription(
pubsub::Topic(project_id, std::move(dl_topic_id)),
pubsub::Subscription(project_id, dl_subscription_id)
);
if (dl_sub.status().code() == google::cloud::StatusCode::kAlreadyExists)
{
;
}
else if (! dl_sub)
{
throw std::runtime_error(sub.status().message());
}
else
{
LOGd << "The deadletter subscription " << dl_subscription_id << " was successfully created: " << dl_sub->DebugString();
}
return;
}
(client, pubSubProjectId, topicId, subscriptionId, dlTopicId, dlSubscriptionId);
}
void run()
{
while (running)
{
try
{
namespace pubsub = ::google::cloud::pubsub;;
int concurrency = 1;
pubsub::TopicAdminClient topicClient(pubsub::MakeTopicAdminConnection());
pubsub::SubscriptionAdminClient subscriptionClient(pubsub::MakeSubscriptionAdminConnection());
createTopicSubscriptionId(eId, fId, "", fTopicId, fSubscriptionId, fDLTopicId, fDLSubscriptionId);
createTopic(fTopicId, fDLTopicId, topicClient);
createOrderingSubscription(fTopicId, fSubscriptionId, fDLTopicId, fDLSubscriptionId, subscriptionClient);
fSubscriptionProblem = false;
auto const subscription = pubsub::Subscription(pubSubProjectId, fSubscriptionId);
auto subscriber = pubsub::Subscriber(pubsub::MakeSubscriberConnection(
subscription,
pubsub::SubscriberOptions{}
.set_max_outstanding_messages(concurrency)
.set_max_outstanding_bytes(concurrency * 1024)
.set_max_concurrency(concurrency),
pubsub::ConnectionOptions{}.set_background_thread_pool_size(concurrency))
);
auto session = subscriber.Subscribe([](pubsub::Message const& m, pubsub::AckHandler h) {
ProcessMsg::CommTopic topic = static_cast<ProcessMsg::CommTopic>(std::stoi(m.attributes()["topic"]));
bool g = true;
std::string msg = m.data();
LOGi << "Received message, id " << m.message_id() << ", attempt " << h.delivery_attempt() <<", ordering key '" << m.ordering_key() << "', topic " << ProcessMsg::commTopic(topic) << " (" << topic << ")";
messageCallback(msg);
std::move(h).ack();
})
.then([](::google::cloud::future<google::cloud::Status> f) {
LOGe << "Subscription problem : " << f.get();
fSubscriptionProblem = true;
});
if (! session.valid())
{
LOGe << "Subscribe session invalid";
break;
}
while (running && ! fSubscriptionProblem)
{
if (outgoingQueue.empty())
{
std::this_thread::sleep_for(std::chrono::milliseconds(500));
continue;
}
std::string gTopicId = "";
std::string gSubscriptionId = "";
std::string gDLTopicId = "";
std::string gDLSubscriptionId = "";
const OutgoingMsg omsg = outgoingQueue.front();
createTopicSubscriptionId(0, 0, omsg.identifier, gTopicId, gSubscriptionId, gDLTopicId, gDLSubscriptionId);
createTopic(gTopicId, gDLTopicId, topicClient);
createOrderingSubscription(gTopicId, gSubscriptionId, gDLTopicId, gDLSubscriptionId, subscriptionClient);
try
{
auto publisher = pubsub::Publisher(pubsub::MakePublisherConnection(pubsub::Topic(pubSubProjectId, gTopicId)));
auto id = publisher.Publish(pubsub::MessageBuilder{}
.SetData(omsg.msg)
.SetAttribute("timestamp", std::to_string(time(NULL)))
.Build()).get();
if (! id)
{
LOGe << "Failed to publish message " << id.status().message();
}
else
{
LOGd << "Published message, id : " << *id << " (" << omsg.msg << ") to " << pubSubProjectId << "/" << gTopicId;
outgoingQueue.pop_front();
}
}
catch (std::exception const& e)
{
LOGe << "Failed to publish message " << e.what();
}
}
if (running)
{
LOGn << "GCP problem, cancelling session";
session.cancel();
LOGd << "Waiting 15 seconds";
std::this_thread::sleep_for(std::chrono::seconds(15));
}
}
catch (std::runtime_error &e)
{
LOGe << "RuntimeError exception : " << e.what();
}
catch(std::exception& e)
{
LOGe << "Exception : " << e.what();
}
}
}

Boost ASIO async_read is not reading data from the server

I have a server/client application which works for a write from client to a read at server.
After the sever is done receiving the data in the function read_async_1, it writes a simple string "Response" at the end.
Now, this is not received at the client. In the client code, StartHandlingServer is what does an async read.. Now, the handler inside this is not getting called..
Can someone please have a look at this ? Appreciate your feedback.
Server Code
#include <boost/asio.hpp>
#include <boost/archive/text_iarchive.hpp>
#include <boost/archive/text_oarchive.hpp>
#include <boost/bind.hpp>
#include <boost/serialization/vector.hpp>
#include <boost/tuple/tuple.hpp>
#include <thread>
#include <atomic>
#include <memory>
#include <iostream>
#include "../stocks.hpp"
using namespace boost;
class Service {
public:
Service(){}
void StartHandligClient(boost::shared_ptr<asio::ip::tcp::socket> sock)
{
std::cout << "StartHandligClient : sock.use_count : " << sock.use_count() << "\n";
read_async_1(sock);
return;
}
private:
void read_async_1(boost::shared_ptr<asio::ip::tcp::socket> sock)
{
if(!(*sock.get()).is_open())
{
std::cout << getpid() << " : Socket closed in sync_read \n" << std::flush;
return ;
}
std::cout << "haha_1\n" << std::flush;
boost::asio::async_read( (*sock.get()), boost::asio::buffer(inbound_header_),
[this, sock](boost::system::error_code ec,
size_t bytesRead)
{
int headerBytesReceived = bytesRead;
std::cout << "\n\n headerBytesReceived : " << headerBytesReceived << "\n" << std::flush ;
if (!ec)
{
// Determine the length of the serialized data.
std::istringstream is(std::string(inbound_header_, header_length));
std::cout << "is : +" << is.str() << "+, inbound_header_ : +" << inbound_header_ << "+\n";
std::size_t inbound_data_size = 0;
if (!(is >> std::hex >> inbound_data_size))
{
// Header doesn't seem to be valid. Inform the caller.
// boost::system::error_code error(boost::asio::error::invalid_argument);
// boost::get<0>(handler)(error);
std::cout << "RET-1 \n";
return;
}
std::cout << "inbound_data_size : " << inbound_data_size << "\n" << std::flush;
// Start an asynchronous call to receive the data.
inbound_data_.resize(inbound_data_size);
std::cout << "inbound_data_.size() : " << inbound_data_.size() << "\n" << std::flush;
int bytesReceived = asio::read( *sock.get(), boost::asio::buffer(inbound_data_) );
std::string archive_data(&inbound_data_[0], inbound_data_.size());
std::istringstream archive_stream(archive_data);
boost::archive::text_iarchive archive(archive_stream);
archive >> stocks_;
std::cout << "bytesReceived : " << bytesReceived << " , stocks_.size() : " << stocks_.size() << "\n";
// Print out the data that was received.
for (std::size_t i = 0; i < stocks_.size(); ++i)
{
std::cout << "Stock number " << i << "\n";
std::cout << " code: " << stocks_[i].code << "\n";
std::cout << " name: " << stocks_[i].name << "\n";
std::cout << " open_price: " << stocks_[i].open_price << "\n";
std::cout << " high_price: " << stocks_[i].high_price << "\n";
std::cout << " low_price: " << stocks_[i].low_price << "\n";
std::cout << " last_price: " << stocks_[i].last_price << "\n";
std::cout << " buy_price: " << stocks_[i].buy_price << "\n";
std::cout << " buy_quantity: " << stocks_[i].buy_quantity << "\n";
std::cout << " sell_price: " << stocks_[i].sell_price << "\n";
std::cout << " sell_quantity: " << stocks_[i].sell_quantity << "\n";
}
sleep(1);
// Sending response.
std::string response = "Response\n";
asio::write(*sock.get(), asio::buffer(response));
this->read_async_1(sock);
}
else
{
// Terminate connection ?
if(ec == boost::asio::error::eof)
{
std::cout << getpid() << " : ** sync_read : Connection lost : boost::asio::error::eof ** \n";
}
std::cout << "Error occured in async_read! Error code = " << ec.value() << ". Message: " << ec.message() << "\n" << std::flush;
return ;
}
return ;
}
);
std::cout << getpid() << " : final return from async_read \n" << std::flush;
return ;
}
/// The size of a fixed length header.
enum { header_length = 8 };
/// Holds an outbound header.
std::string outbound_header_;
/// Holds the outbound data.
std::string outbound_data_;
/// Holds an inbound header.
char inbound_header_[header_length];
/// Holds the inbound data.
std::vector<char> inbound_data_;
std::vector<stock> stocks_;
};
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_acceptor.listen();
}
void Accept() {
std::cout << "Server Accept() \n" << std::flush;
boost::shared_ptr<asio::ip::tcp::socket> sock(new asio::ip::tcp::socket(m_ios));
m_acceptor.accept(*sock.get());
(new Service)->StartHandligClient(sock);
std::cout << "Accept : sock.use_count : " << sock.use_count() << "\n";
}
private:
asio::io_service& m_ios;
asio::ip::tcp::acceptor m_acceptor;
};
class Server {
public:
Server() : m_stop(false) {}
void Start(unsigned short port_num) {
m_thread.reset(new std::thread([this, port_num]() {
Run(port_num);
}));
}
void Stop() {
std::cout << "STOPPING \n";
m_stop.store(true);
m_thread->join();
}
private:
void Run(unsigned short port_num) {
Acceptor acc(m_ios, port_num);
while (!m_stop.load())
{
std::cout << "Server accept\n" << std::flush;
acc.Accept();
m_ios.run();
}
}
std::unique_ptr<std::thread> m_thread;
std::atomic<bool> m_stop;
asio::io_service m_ios;
};
int main()
{
unsigned short port_num = 3333;
try {
Server srv;
srv.Start(port_num);
std::this_thread::sleep_for(std::chrono::seconds(100));
std::cout << "Stopping server \n";
srv.Stop();
}
catch (system::system_error &e) {
std::cout << "Error occured! Error code = "
<< e.code() << ". Message: "
<< e.what();
}
return 0;
}
Client Code
#include <boost/asio.hpp>
#include <boost/archive/text_iarchive.hpp>
#include <boost/archive/text_oarchive.hpp>
#include <boost/bind.hpp>
#include <boost/serialization/vector.hpp>
#include <iostream>
#include <boost/shared_ptr.hpp>
#include "../stocks.hpp"
using namespace boost;
class mysock : public boost::asio::ip::tcp::socket
{
public:
mysock(asio::io_service& serv) : boost::asio::ip::tcp::socket(serv)
{
}
~mysock()
{
std::cout << "Inside destructor for mysock \n";
}
};
class SyncTCPClient {
public:
SyncTCPClient(const std::string& raw_ip_address,
unsigned short port_num) :
socket_((new mysock(m_ios))),
m_ep(asio::ip::address::from_string(raw_ip_address), port_num)
{
(*socket_.get()).open(m_ep.protocol());
connect();
StartHandlingServer(socket_);
}
mysock& socket()
{
return *socket_.get();
}
void connect() {
(*socket_.get()).connect(m_ep);
m_ios.run();
}
void StartHandlingServer(boost::shared_ptr<mysock> sock)
{
if(!(*sock.get()).is_open())
{
std::cout << getpid() << " : Socket closed in sync_read \n" << std::flush;
return ;
}
std::cout << "Start StartHandlingServer\n" << std::flush;
char inbound_header_[4];;
try
{
boost::asio::async_read( (*sock.get()), boost::asio::buffer(inbound_header_),
[this, sock](boost::system::error_code ec,
size_t bytesRead)
{
int headerBytesReceived = bytesRead;
std::cout << "\n\n headerBytesReceived : " << headerBytesReceived << "\n" << std::flush ;
if (!ec)
{
}
else
{
if(ec == boost::asio::error::eof)
{
std::cout << getpid() << " : ** sync_read : Connection lost : boost::asio::error::eof ** \n";
}
std::cout << "Error occured in async_read! Error code = " << ec.value() << ". Message: " << ec.message() << "\n" << std::flush;
return ;
}
}
);
}
catch (std::exception& e)
{
std::cerr << e.what() << std::endl;
}
std::cout << "Done StartHandlingServer\n" << std::flush;
}
void close() {
(*socket_.get()).shutdown(
boost::asio::ip::tcp::socket::shutdown_both);
(*socket_.get()).close();
}
std::string emulateLongComputationOp(
unsigned int duration_sec) {
std::string request = "EMULATE_LONG_COMP_OP "
+ std::to_string(duration_sec)
+ "\n";
sendRequest(request);
/*
sleep(2);
sendRequest(request);
sleep(2);
sendRequest(request);
*/
return receiveResponse();
};
private:
void sendRequest(const std::string& request) {
std::vector<stock> stocks_;
// Create the data to be sent to each client.
stock s;
s.code = "ABC";
s.name = "A Big Company";
s.open_price = 4.56;
s.high_price = 5.12;
s.low_price = 4.33;
s.last_price = 4.98;
s.buy_price = 4.96;
s.buy_quantity = 1000;
s.sell_price = 4.99;
s.sell_quantity = 2000;
stocks_.push_back(s);
// Serialize the data first so we know how large it is.
std::ostringstream archive_stream;
boost::archive::text_oarchive archive(archive_stream);
archive << stocks_;
outbound_data_ = archive_stream.str();
std::cout << "outbound_data_ : " << outbound_data_ << "\n" << std::flush;
std::cout << "outbound_data_.size() : " << outbound_data_.size() << "\n" << std::flush;
// Format the header.
std::ostringstream header_stream;
header_stream << std::setw(header_length) << std::hex << outbound_data_.size();
std::cout << "header_stream.str() : " << header_stream.str() << "\n" << std::flush;
std::cout << "header_stream.str().size() : " << header_stream.str().size() << "\n" << std::flush;
if (!header_stream || header_stream.str().size() != header_length)
{
// Something went wrong, inform the caller.
// boost::system::error_code error(boost::asio::error::invalid_argument);
// socket_.get_io_service().post(boost::bind(handler, error));
return;
}
outbound_header_ = header_stream.str();
std::cout << "outbound_header_ : +" << outbound_header_ << "+\n" << std::flush;
// Write the serialized data to the socket. We use "gather-write" to send
// both the header and the data in a single write operation.
/*
std::vector<boost::asio::const_buffer> buffers;
buffers.push_back(boost::asio::buffer(outbound_header_));
buffers.push_back(boost::asio::buffer(outbound_data_));
*/
std::size_t headerSize = asio::write(*socket_.get(), boost::asio::buffer(outbound_header_));
std::size_t dataSize = asio::write(*socket_.get(), boost::asio::buffer(outbound_data_));
std::cout << "headerSize : " << headerSize << " , dataSize : " << dataSize;
}
std::string receiveResponse() {
std::string response;
/*
asio::streambuf buf;
asio::read_until(*socket_.get(), buf, '\n');
std::istream input(&buf);
std::getline(input, response);
*/
return response;
}
private:
asio::io_service m_ios;
boost::shared_ptr<mysock> socket_;
asio::ip::tcp::endpoint m_ep;
enum { header_length = 8 };
std::string outbound_data_;
std::string outbound_header_;
};
int main()
{
const std::string raw_ip_address = "127.0.0.1";
const unsigned short port_num = 3333;
try {
SyncTCPClient client(raw_ip_address, port_num);
std::cout << "Sending request to the server... \n"<< std::endl;
std::string response = client.emulateLongComputationOp(10);
std::cout << "\nResponse received: " << response << std::endl;
sleep(10);
std::cout << "\n\n Closing client connection \n\n";
// Close the connection and free resources.
client.close();
}
catch (system::system_error &e) {
std::cout << "Client Error occured! Error code = " << e.code()
<< ". Message: " << e.what();
return e.code().value();
}
return 0;
}
Included file stocks.hpp
#ifndef _STOCKS_HPP_
#define _STOCKS_HPP_
struct stock
{
std::string code;
std::string name;
double open_price;
double high_price;
double low_price;
double last_price;
double buy_price;
int buy_quantity;
double sell_price;
int sell_quantity;
template <typename Archive>
void serialize(Archive& ar, const unsigned int version)
{
ar & code;
ar & name;
ar & open_price;
ar & high_price;
ar & low_price;
ar & last_price;
ar & buy_price;
ar & buy_quantity;
ar & sell_price;
ar & sell_quantity;
}
};
#endif
The io_service::run function runs until there are no more events.
In the client you run it once, when there are no active events to be handled, which means it will return immediately.
Because the io_server isn't "running" it will not handle any events.
You need to call run (or poll) in a loop, like you do in the server.

Boost ASIO async_read is not reading data from the client

I have a server/client application which works for a write from client to a read at server.
Inside the startHandlig function in the server code, if I comment async_connect_1 and the return after it, then it works fine which involves sync write function.
I added async_connect_1 function inside Service() class to asynchronously read from the socket.
This function is called when a client connects to the server and this function returns immediately.
I expect the callback function corresponding to async_read to be called, but that is not happening...
I'm stuck at this since a long time.. Appreciate help on this...
Server Code
#include <boost/asio.hpp>
#include <boost/archive/text_iarchive.hpp>
#include <boost/archive/text_oarchive.hpp>
#include <boost/bind.hpp>
#include <boost/serialization/vector.hpp>
#include <boost/tuple/tuple.hpp>
#include <thread>
#include <atomic>
#include <memory>
#include <iostream>
#include "../stocks.hpp"
using namespace boost;
class Service {
public:
Service(){}
void StartHandligClient(
std::shared_ptr<asio::ip::tcp::socket> sock) {
read_async_1(sock);
return;
std::thread th(([this, sock]() {
HandleClient(sock);
}));
std::cout << "Detached \n";
th.detach();
}
private:
void read_async_1(std::shared_ptr<asio::ip::tcp::socket> sock)
{
if(!(*sock.get()).is_open())
{
std::cout << getpid() << " : Socket closed in sync_read \n" << std::flush;
return ;
}
std::cout << "haha_1\n" << std::flush;
boost::asio::async_read( (*sock.get()), boost::asio::buffer(inbound_header_),
[this](boost::system::error_code ec,
size_t bytesRead)
{
std::cout << "haha_2\n" << std::flush;
if (!ec)
{
int headerBytesReceived = bytesRead;
std::cout << "\n\n headerBytesReceived : " << headerBytesReceived << "\n" << std::flush ;
// this->async_read(sock);
}
else
{
// Terminate connection ?
if(ec == boost::asio::error::eof)
{
std::cout << getpid() << " : ** sync_read : Connection lost : boost::asio::error::eof ** \n";
}
std::cout << "Error occured in sync_read! Error code = " << ec.value() << ". Message: " << ec.message() << "\n" << std::flush;
return ;
}
return ;
}
);
std::cout << getpid() << " : final return from async_read \n" << std::flush;
return ;
}
void HandleClient(std::shared_ptr<asio::ip::tcp::socket> sock) {
while(1)
{
try {
// asio::streambuf request;
// asio::read_until(*sock.get(), request, '\n');
int headerBytesReceived = asio::read( *sock.get(), boost::asio::buffer(inbound_header_) );
std::cout << "headerBytesReceived : " << headerBytesReceived << "\n" << std::flush;
// Determine the length of the serialized data.
std::istringstream is(std::string(inbound_header_, header_length));
std::cout << "is : " << is.str() << ", inbound_header_ : " << inbound_header_ << "\n";
std::size_t inbound_data_size = 0;
if (!(is >> std::hex >> inbound_data_size))
{
// Header doesn't seem to be valid. Inform the caller.
// boost::system::error_code error(boost::asio::error::invalid_argument);
// boost::get<0>(handler)(error);
std::cout << "RET-1 \n";
return;
}
std::cout << "inbound_data_size : " << inbound_data_size << "\n" << std::flush;
// Start an asynchronous call to receive the data.
inbound_data_.resize(inbound_data_size);
std::cout << "inbound_data_.size() : " << inbound_data_.size() << "\n" << std::flush;
int bytesReceived = asio::read( *sock.get(), boost::asio::buffer(inbound_data_) );
std::string archive_data(&inbound_data_[0], inbound_data_.size());
std::istringstream archive_stream(archive_data);
boost::archive::text_iarchive archive(archive_stream);
archive >> stocks_;
std::cout << "bytesReceived : " << bytesReceived << " , stocks_.size() : " << stocks_.size() << "\n";
// Print out the data that was received.
for (std::size_t i = 0; i < stocks_.size(); ++i)
{
std::cout << "Stock number " << i << "\n";
std::cout << " code: " << stocks_[i].code << "\n";
std::cout << " name: " << stocks_[i].name << "\n";
std::cout << " open_price: " << stocks_[i].open_price << "\n";
std::cout << " high_price: " << stocks_[i].high_price << "\n";
std::cout << " low_price: " << stocks_[i].low_price << "\n";
std::cout << " last_price: " << stocks_[i].last_price << "\n";
std::cout << " buy_price: " << stocks_[i].buy_price << "\n";
std::cout << " buy_quantity: " << stocks_[i].buy_quantity << "\n";
std::cout << " sell_price: " << stocks_[i].sell_price << "\n";
std::cout << " sell_quantity: " << stocks_[i].sell_quantity << "\n";
}
}
catch (system::system_error &e)
{
boost::system::error_code ec = e.code();
if(ec == boost::asio::error::eof)
{
std::cout << "EOF Error \n";
}
std::cout << "Server Error occured! Error code = "
<< e.code() << ". Message: "
<< e.what() << "\n";
break;
}
}
// Clean-up.
delete this;
}
/// The size of a fixed length header.
enum { header_length = 8 };
/// Holds an outbound header.
std::string outbound_header_;
/// Holds the outbound data.
std::string outbound_data_;
/// Holds an inbound header.
char inbound_header_[header_length];
/// Holds the inbound data.
std::vector<char> inbound_data_;
std::vector<stock> stocks_;
};
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_acceptor.listen();
}
void Accept() {
std::cout << "Server Accept() \n" << std::flush;
std::shared_ptr<asio::ip::tcp::socket>
sock(new asio::ip::tcp::socket(m_ios));
m_acceptor.accept(*sock.get());
(new Service)->StartHandligClient(sock);
}
private:
asio::io_service& m_ios;
asio::ip::tcp::acceptor m_acceptor;
};
class Server {
public:
Server() : m_stop(false) {}
void Start(unsigned short port_num) {
m_thread.reset(new std::thread([this, port_num]() {
Run(port_num);
}));
}
void Stop() {
m_stop.store(true);
m_thread->join();
}
private:
void Run(unsigned short port_num) {
Acceptor acc(m_ios, port_num);
while (!m_stop.load()) {
std::cout << "Server accept\n" << std::flush;
acc.Accept();
}
}
std::unique_ptr<std::thread> m_thread;
std::atomic<bool> m_stop;
asio::io_service m_ios;
};
int main()
{
unsigned short port_num = 3333;
try {
Server srv;
srv.Start(port_num);
std::this_thread::sleep_for(std::chrono::seconds(100));
std::cout << "Stopping server \n";
srv.Stop();
}
catch (system::system_error &e) {
std::cout << "Error occured! Error code = "
<< e.code() << ". Message: "
<< e.what();
}
return 0;
}
Client Code
#include <boost/asio.hpp>
#include <boost/archive/text_iarchive.hpp>
#include <boost/archive/text_oarchive.hpp>
#include <boost/bind.hpp>
#include <boost/serialization/vector.hpp>
#include <iostream>
#include "../stocks.hpp"
using namespace boost;
class SyncTCPClient {
public:
SyncTCPClient(const std::string& raw_ip_address,
unsigned short port_num) :
m_ep(asio::ip::address::from_string(raw_ip_address),
port_num),
m_sock(m_ios) {
m_sock.open(m_ep.protocol());
}
void connect() {
m_sock.connect(m_ep);
}
void close() {
m_sock.shutdown(
boost::asio::ip::tcp::socket::shutdown_both);
m_sock.close();
}
std::string emulateLongComputationOp(
unsigned int duration_sec) {
std::string request = "EMULATE_LONG_COMP_OP "
+ std::to_string(duration_sec)
+ "\n";
sendRequest(request);
return receiveResponse();
};
private:
void sendRequest(const std::string& request) {
std::vector<stock> stocks_;
// Create the data to be sent to each client.
stock s;
s.code = "ABC";
s.name = "A Big Company";
s.open_price = 4.56;
s.high_price = 5.12;
s.low_price = 4.33;
s.last_price = 4.98;
s.buy_price = 4.96;
s.buy_quantity = 1000;
s.sell_price = 4.99;
s.sell_quantity = 2000;
stocks_.push_back(s);
// Serialize the data first so we know how large it is.
std::ostringstream archive_stream;
boost::archive::text_oarchive archive(archive_stream);
archive << stocks_;
outbound_data_ = archive_stream.str();
std::cout << "outbound_data_ : " << outbound_data_ << "\n" << std::flush;
std::cout << "outbound_data_.size() : " << outbound_data_.size() << "\n" << std::flush;
// Format the header.
std::ostringstream header_stream;
header_stream << std::setw(header_length) << std::hex << outbound_data_.size();
std::cout << "header_stream.str() : " << header_stream.str() << "\n" << std::flush;
std::cout << "header_stream.str().size() : " << header_stream.str().size() << "\n" << std::flush;
if (!header_stream || header_stream.str().size() != header_length)
{
// Something went wrong, inform the caller.
// boost::system::error_code error(boost::asio::error::invalid_argument);
// socket_.get_io_service().post(boost::bind(handler, error));
return;
}
outbound_header_ = header_stream.str();
std::cout << "outbound_header_ : " << outbound_header_ << "\n" << std::flush;
// Write the serialized data to the socket. We use "gather-write" to send
// both the header and the data in a single write operation.
std::vector<boost::asio::const_buffer> buffers;
buffers.push_back(boost::asio::buffer(outbound_header_));
buffers.push_back(boost::asio::buffer(outbound_data_));
std::size_t sizeSent = asio::write(m_sock, buffers);
std::cout << "sizeSent : " << sizeSent << "\n" << std::flush;
}
std::string receiveResponse() {
std::string response;
/*
asio::streambuf buf;
asio::read_until(m_sock, buf, '\n');
std::istream input(&buf);
std::getline(input, response);
*/
return response;
}
private:
asio::io_service m_ios;
asio::ip::tcp::endpoint m_ep;
asio::ip::tcp::socket m_sock;
enum { header_length = 8 };
std::string outbound_data_;
std::string outbound_header_;
};
int main()
{
const std::string raw_ip_address = "127.0.0.1";
const unsigned short port_num = 3333;
try {
SyncTCPClient client(raw_ip_address, port_num);
// Sync connect.
client.connect();
sleep(1);
std::cout << "Sending request to the server... "
<< std::endl;
std::string response = client.emulateLongComputationOp(10);
std::cout << "Response received: " << response << std::endl;
sleep(100);
std::cout << "\n\n Closing client connection \n\n";
// Close the connection and free resources.
client.close();
}
catch (system::system_error &e) {
std::cout << "Client Error occured! Error code = " << e.code()
<< ". Message: " << e.what();
return e.code().value();
}
return 0;
}
Included File (stocks.hpp)
#ifndef _STOCKS_HPP_
#define _STOCKS_HPP_
struct stock
{
std::string code;
std::string name;
double open_price;
double high_price;
double low_price;
double last_price;
double buy_price;
int buy_quantity;
double sell_price;
int sell_quantity;
template <typename Archive>
void serialize(Archive& ar, const unsigned int version)
{
ar & code;
ar & name;
ar & open_price;
ar & high_price;
ar & low_price;
ar & last_price;
ar & buy_price;
ar & buy_quantity;
ar & sell_price;
ar & sell_quantity;
}
};
#endif
You have written Error code = 125. Message: Operation canceled as comment in previous response, i think that socket may be closed before async operation will be done.
What is lifetime of your socket ?
[1] socket is created in Accept method
std::shared_ptr<asio::ip::tcp::socket>
sock(new asio::ip::tcp::socket(m_ios)); // ref count +1
//...
(new Service)->StartHandligClient(sock); // this function returns immediately
// so socket's ref count -1
[2] in StartHandligClient()
sock is passed by value, so ref count of socket +1, but
void StartHandligClient(
std::shared_ptr<asio::ip::tcp::socket> sock) { // +1 ref count
read_async_1(sock); // this function returns immediately
return; // -1 ref count of socket
}
[3] in read_async_1 socket is passed by value, +1 on ref count of socket, but this function returns immediately, when function ends, ref count is decreased and socket object is deleted.
You created lambda object to execute asynchronus operation, but socket object may be closed before doing it.
You did apparently use a asio::io_service, but you forgot to run it.
m_ios.run();
Run the io_context object's event processing loop.
Fix this and your handler[s] will be called.
You can either create a thread for this, or call it in your main function in your 'main-thread'.
std::thread([this]() { m_ios.run(); } );
Note: Don't forget to stop (1) it later and join the thread (2) if you created one.

Connecting to an IRC server, getting Ping timeout without receiving PING message

I'm trying my hands at network programming for the first time, implementing a small IRC bot using the SFML network functionality.
The connection gets established, but from there on I can't do much else. Trying to receive any data from the server yields nothing, until I get the "Ping timeout" message after a few seconds.
Removing all or some of the receive() calls in the loginOnIRC function doesn't do any good.
Trying to connect via telnet with the exact same messages works. Here I get a PING message right after sending my NICK message.
Am I missing something?
My code is as follows
#include <iostream>
#include <string>
#include <SFML/Network.hpp>
#define ARRAY_LEN(x) (sizeof(x)/sizeof(*x))
void receive(sf::TcpSocket* sck)
{
char rcvData[100];
memset(rcvData, 0, ARRAY_LEN(rcvData));
std::size_t received;
if (sck->receive(rcvData, ARRAY_LEN(rcvData), received) != sf::Socket::Done)
{
std::cout << "oops" << std::endl;
}
std::cout << "Received " << received << " bytes" << std::endl;
std::cout << rcvData << std::endl;
}
int establishConnection(sf::TcpSocket* sck)
{
sf::Socket::Status status = sck->connect("irc.euirc.net", 6667, sf::seconds(5.0f));
if (status != sf::Socket::Done)
{
std::cout << "Error on connect!" << std::endl;
return 1;
}
std::cout << "Connect was successful!" << std::endl;
return 0;
}
int loginOnIRC(sf::TcpSocket* sck)
{
receive(sck); // We get a Ping timeout here
std::string data{ "NICK NimBot" };
if(sck->send(data.c_str(), data.length()) != sf::Socket::Done)
{
std::cout << "Error on sending " << data << std::endl;
return 1;
}
receive(sck);
data = "USER NimBot * * :Nimelrians Bot";
if (sck->send(data.c_str(), data.length()) != sf::Socket::Done)
{
std::cout << "Error on sending " << data << std::endl;
return 1;
}
receive(sck);
data = "JOIN #nimbottest";
if (sck->send(data.c_str(), data.length()) != sf::Socket::Done)
{
std::cout << "Error on sending " << data << std::endl;
return 1;
}
return 0;
}
int main()
{
sf::TcpSocket sck{};
establishConnection(&sck); // works
loginOnIRC(&sck);
while(true)
{
char data[100];
memset(data, 0, ARRAY_LEN(data));
std::size_t received;
sf::Socket::Status rcvStatus = sck.receive(data, ARRAY_LEN(data), received);
if (rcvStatus != sf::Socket::Done)
{
std::cout << "oops" << std::endl;
if (rcvStatus == sf::Socket::Disconnected)
{
break;
}
}
std::cout << "Received " << received << " bytes" << std::endl;
std::cout << data << std::endl;
}
return 0;
}