I have some simple application:
//first.exe
int main()
{
std::cout << "1" << std::endl;
std::cout << "2" << std::endl;
std::cout << "3" << std::endl;
std::cin.get();
return 0;
}
And I want to invoke a callback in separate application when "2" occurs in stdout of application run as a child process:
//second.exe
int main()
{
boost::asio::io_context context;
boost::process::async_pipe out(context);
boost::asio::streambuf buffer;
boost::process::child("first.exe", boost::process::std_out > out);
boost::asio::async_read_until(out, buffer, "2", [](boost::system::error_code code, std::size_t size)
{
//do something
});
context.run();
return 0;
}
The problem is that I have to press any button in parent process due to std::cin.get() in child process to make io_context execute callback function. Do you have any suggestions to make it work as intended?
In this case I don't think you need to do a thing (the std::cin read might just fail):
Live On Coliru
#include <boost/process.hpp>
#include <boost/asio.hpp>
#include <boost/process/async.hpp>
#include <iostream>
#include <iomanip>
int main()
{
namespace bp = boost::process;
boost::asio::io_context context;
bp::async_pipe out(context);
bp::child c("./first.exe", bp::std_out > out);
boost::asio::streambuf buffer;
boost::asio::async_read_until(out, buffer, "2", [&](boost::system::error_code code, std::size_t size) {
if (code) {
std::cerr << "Oops: " << code.message() << std::endl;
} else {
std::cerr << "received: " << size << " bytes: ";
auto b = buffers_begin(buffer.data()), m = b+size, e = buffers_end(buffer.data());
std::clog << std::quoted(std::string(b, m)) << std::endl;
std::clog << "Note that we read more bytes: " << std::quoted(std::string(m, e)) << std::endl;
buffer.consume(size);
}
});
context.run();
return c.exit_code();
}
Prints
received: 3 bytes: "1
2"
Note that we read more bytes: "
3
"
Cleaner
For cleanness, you could simply close std_in:
bp::child c("./first.exe", bp::std_out > out, bp::std_in.close());
And to be proper, also add the context for async io:
bp::child c("./first.exe", bp::std_out > out, bp::std_in.close(), context);
Those all work (see live).
More complicated
What if you actually need to supply input to get output? Or you need to supply input based on the output? Then the commenter is right: attach a pipe to std_in (or have a buffer be written asynchronously).
Sending a Fixed Buffer
Live On Coliru
first.cpp
#include <iostream>
int main() {
using namespace std;
string s;
while (cin >> s)
cout << "reversed: " << string(s.rbegin(), s.rend()) << endl;
}
main.cpp
#include <boost/process.hpp>
#include <boost/asio.hpp>
#include <boost/process/async.hpp>
#include <iostream>
#include <iomanip>
int main() {
namespace bp = boost::process;
boost::asio::io_context context;
bp::async_pipe out(context);
std::string i = "hello\nwo2rld\n";
bp::child c("./first.exe", bp::std_out > out, bp::std_in < boost::asio::buffer(i), context);
boost::asio::streambuf buffer;
boost::asio::async_read_until(out, buffer, "2", [&](boost::system::error_code code, std::size_t size) {
if (code) {
std::cerr << "Oops: " << code.message() << std::endl;
} else {
std::cerr << "received: " << size << " bytes: ";
auto b = buffers_begin(buffer.data()), m = b+size, e = buffers_end(buffer.data());
std::clog << std::quoted(std::string(b, m)) << std::endl;
std::clog << "Note that we read more bytes: " << std::quoted(std::string(m, e)) << std::endl;
buffer.consume(size);
}
});
context.run();
return c.exit_code();
}
Prints
received: 30 bytes: "reversed: olleh
reversed: dlr2"
Note that we read more bytes: "ow
"
Sending Dynamic Data
Both synchronously and asynchronously:
#include <boost/process.hpp>
#include <boost/asio.hpp>
#include <boost/process/async.hpp>
#include <iostream>
#include <iomanip>
int main() {
namespace bp = boost::process;
boost::asio::io_context context;
bp::async_pipe out(context), in(context);
bp::child c("./first.exe", bp::std_out > out, bp::std_in < in, context);
boost::asio::write(in, boost::asio::buffer("hello ", 6));
boost::asio::streambuf buffer;
boost::asio::async_read_until(out, buffer, "2", [&](boost::system::error_code code, std::size_t size) {
if (code) {
std::cerr << "Oops: " << code.message() << std::endl;
} else {
std::cerr << "received: " << size << " bytes: ";
auto b = buffers_begin(buffer.data()), m = b+size, e = buffers_end(buffer.data());
std::clog << std::quoted(std::string(b, m)) << std::endl;
std::clog << "Note that we read more bytes: " << std::quoted(std::string(m, e)) << std::endl;
buffer.consume(size);
}
});
boost::asio::async_write(in, boost::asio::buffer("wo2rld\n", 7), [&](boost::system::error_code code, std::size_t size) {
if (code) {
std::cerr << "Oops: " << code.message() << std::endl;
} else {
std::cerr << "sent: " << size << " bytes: ";
}
});
context.run();
return c.exit_code();
}
Printing again:
sent: 7 bytes: received: 30 bytes: "reversed: olleh
reversed: dlr2"
Note that we read more bytes: "ow
"
Related
I'm starting a process through boost::process. The process uses std::cout and std::cerr to output some information. I need to retrieve those information. At some point, I want to be able to store those outputs preserving order and severity (output from cout or cerr).
But I could not achieve that considering the way boost::process redirects the outputs. I could only redirect std::cout to a specific ipstream and std::cerr to another. Then, when reading them, I can't preserve the order.
Here is a MCVE isolating teh problem:
#include <iostream>
#include <boost/process.hpp>
#include <boost/thread.hpp>
#include <boost/bind.hpp>
void doReadOutput( boost::process::ipstream* is, boost::process::ipstream* err, std::ostream* out )
{
std::string line;
if ( std::getline( *is, line ) )
*out << "cout: " << line << std::endl;
if ( std::getline( *err, line ) )
*out << "cerr: " << line << std::endl;
}
void readOutput( boost::process::ipstream* is, boost::process::ipstream* err, std::ostream* out, std::atomic_bool* continueFlag )
{
std::string line;
while ( *continueFlag )
{
doReadOutput( is, err, out );
}
// get last outputs that may remain in buffers
doReadOutput( is, err, out );
}
int main( int argc, char* argv[] )
{
if ( argc == 1 )
{
// run this same program with "foo" as parameter, to enter "else" statement below from a different process
try
{
boost::process::ipstream is_stream, err_stream;
std::stringstream merged_output;
std::atomic_bool continueFlag = true;
boost::process::child child( argv[0],
std::vector<std::string>{ "foo" },
boost::process::std_out > is_stream,
boost::process::std_err > err_stream );
boost::thread thrd( boost::bind( readOutput, &is_stream, &err_stream, &merged_output, &continueFlag ) );
child.wait();
continueFlag = false;
thrd.join();
std::cout << "Program output was:" << std::endl;
std::cout << merged_output.str();
}
catch ( const boost::process::process_error& err )
{
std::cerr << "Error: " << err.code() << std::endl;
}
catch (...) // #NOCOVERAGE
{
std::cerr << "Unknown error" << std::endl;
}
}
else
{
// program invoked through boost::process by "if" statement above
std::cerr << "Error1" << std::endl;
std::cout << "Hello World1" << std::endl;
std::cerr << "Error2" << std::endl;
std::cerr << "Error3" << std::endl;
std::cerr << "Error4" << std::endl;
std::cerr << "Error5" << std::endl;
std::cout << "Hello World2" << std::endl;
std::cerr << "Error6" << std::endl;
std::cout << "Hello World3" << std::endl;
}
return 0;
}
When I execute this program (Compiled with Visual Studio 2019 under Windows 10), it outputs:
Program output was:
cout: Hello World1
cerr: Error1
cout: Hello World2
cerr: Error2
cout: Hello World3
cerr: Error3
cerr: Error4
cerr: Error5
cerr: Error6
While I want:
Program output was:
cerr: Error1
cout: Hello World1
cerr: Error2
cerr: Error3
cerr: Error4
cerr: Error5
cout: Hello World2
cerr: Error6
cout: Hello World3
Is there any way to achieve that?
Edit, as suggested by Some programmer dude, created one thread per output stream:
#include <iostream>
#include <boost/process.hpp>
#include <boost/thread.hpp>
#include <boost/bind.hpp>
#include <boost/thread/mutex.hpp>
void doReadOutput( boost::process::ipstream* str, std::ostream* out, const std::string& prefix, boost::mutex* mutex )
{
std::string line;
if ( std::getline( *str, line ) )
{
boost::mutex::scoped_lock lock( *mutex );
*out << prefix << ": " << line << std::endl;
}
}
void readOutput( boost::process::ipstream* str, std::ostream* out, std::string prefix, boost::mutex* mutex, std::atomic_bool* continueFlag )
{
while ( *continueFlag )
{
doReadOutput( str, out, prefix, mutex );
boost::thread::yield();
}
// get last outputs that may remain in buffers
doReadOutput( str, out, prefix, mutex );
}
int main( int argc, char* argv[] )
{
if ( argc == 1 )
{
// run this same program with "foo" as parameter, to enter "else" statement below from a different process
try
{
boost::process::ipstream is_stream, err_stream;
std::stringstream merged_output;
std::atomic_bool continueFlag = true;
boost::process::child child( argv[0],
std::vector<std::string>{ "foo" },
boost::process::std_out > is_stream,
boost::process::std_err > err_stream );
boost::mutex mutex;
boost::thread thrdis( boost::bind( readOutput, &is_stream, &merged_output, "cout", &mutex, &continueFlag ) );
boost::thread thrderr( boost::bind( readOutput, &err_stream, &merged_output, "cerr", &mutex, &continueFlag ) );
child.wait();
continueFlag = false;
thrdis.join();
thrderr.join();
std::cout << "Program output was:" << std::endl;
std::cout << merged_output.str();
}
catch ( const boost::process::process_error& err )
{
std::cerr << "Error: " << err.code() << std::endl;
}
catch (...) // #NOCOVERAGE
{
std::cerr << "Unknown error" << std::endl;
}
}
else
{
// program invoked through boost::process by "if" statement above
std::cerr << "Error1" << std::endl;
std::cout << "Hello World1" << std::endl;
std::cerr << "Error2" << std::endl;
std::cerr << "Error3" << std::endl;
std::cerr << "Error4" << std::endl;
std::cerr << "Error5" << std::endl;
std::cout << "Hello World2" << std::endl;
std::cerr << "Error6" << std::endl;
std::cout << "Hello World3" << std::endl;
}
return 0;
}
Then the output is:
Program output was:
cerr: Error1
cout: Hello World1
cerr: Error2
cout: Hello World2
cerr: Error3
cout: Hello World3
cerr: Error4
cerr: Error5
cerr: Error6
Still unexpected...
You will need non-blocking IO. The supported way in the library is by using asynchronous pipes.
You would run a loop for both stderr/stdout doing
async_read into a buffer until you get a full line or more
copy the line from input buffer to the output buffer as soon as it became available
Because you'll end up having twice very much the same loop over pipe/buffer state, it makes sense to encapsulate it into a type, e.g.
struct IoPump {
IoPump(io_context& io, std::string& merged) : _pipe(io), _merged(merged) {}
boost::asio::streambuf _buf;
bp::async_pipe _pipe;
std::string& _merged;
void do_loop();
};
io_context io;
std::string merged;
IoPump outp{io, merged}, errp{io, merged};
bp::child child(program, std::vector<std::string> { "foo" },
bp::std_out > outp._pipe, bp::std_err > errp._pipe);
outp.do_loop(); // prime the pump
errp.do_loop(); // prime the pump
io.run();
That's all. Well, except of course, what IoPump::do_loop() actually does:
void do_loop() {
boost::asio::async_read_until(_pipe, _buf, "\n",
[this, out = boost::asio::dynamic_buffer(_merged)](
error_code ec, size_t xfer) mutable {
if (!ec) {
out.commit(buffer_copy(
out.prepare(xfer), _buf.data(), xfer));
_buf.consume(xfer);
do_loop(); // chain
} else {
std::cerr << "IoPump: " << ec.message() << "\n";
}
});
}
Note that
your main application is completely single-threaded
meaning that async completion handlers never run concurrently
meaning that it is safe to just access the std::string merged; output buffer directly without worrying about synchronization
Live Demo
Live On Coliru
static void main_program(char const* program);
static void child_program();
int main(int argc, char** argv) {
if (argc == 1)
main_program(argv[0]);
else
child_program();
}
#include <iostream>
static void child_program() {
std::cerr << "Error1" << std::endl;
std::cout << "Hello World1" << std::endl;
std::cerr << "Error2" << std::endl;
std::cerr << "Error3" << std::endl;
std::cerr << "Error4" << std::endl;
std::cerr << "Error5" << std::endl;
std::cout << "Hello World2" << std::endl;
std::cerr << "Error6" << std::endl;
std::cout << "Hello World3" << std::endl;
}
#include <boost/process.hpp>
#include <boost/asio.hpp>
static void main_program(char const* program) {
namespace bp = boost::process;
try {
using boost::system::error_code;
using boost::asio::io_context;
struct IoPump {
IoPump(io_context& io, std::string& merged) : _pipe(io), _merged(merged) {}
boost::asio::streambuf _buf;
bp::async_pipe _pipe;
std::string& _merged;
void do_loop() {
boost::asio::async_read_until(_pipe, _buf, "\n",
[this, out = boost::asio::dynamic_buffer(_merged)](
error_code ec, size_t xfer) mutable {
if (!ec) {
out.commit(buffer_copy(
out.prepare(xfer), _buf.data(), xfer));
_buf.consume(xfer);
do_loop(); // chain
} else {
std::cerr << "IoPump: " << ec.message() << "\n";
}
});
}
};
io_context io;
std::string merged;
IoPump outp{io, merged}, errp{io, merged};
bp::child child(program, std::vector<std::string> { "foo" },
bp::std_out > outp._pipe, bp::std_err > errp._pipe);
outp.do_loop(); // prime the pump
errp.do_loop(); // prime the pump
io.run();
std::cout << "Program output was:" << std::endl;
std::cout << merged;
} catch (const bp::process_error& err) {
std::cerr << "Error: " << err.code().message() << std::endl;
} catch (...) { // #NOCOVERAGE
std::cerr << "Unknown error" << std::endl;
}
}
Prints
IoPump: End of file
IoPump: End of file
And standard output:
Program output was:
Error1
Error2
Hello World1
Error3
Hello World2
Error4
Hello World3
Error5
Error6
Other Examples
I've got many examples on this site already. Just look for async_pipe
Thinking Out Of The Box
You could simply redirect stderr into stdout at the descriptor level and be done! E.g.
Live On Coliru
boost::asio::io_context io;
std::future<std::string> merged;
bp::child child(program, std::vector<std::string> { "foo" },
bp::std_out > merged, bp::posix::fd.bind(2, 1), io);
io.run();
std::cout << "Program output was:" << std::quoted(merged.get()) << "\n";
Or with a line-wise reading loop:
Live On Coliru
bp::ipstream merged;
bp::child child(program, std::vector<std::string> { "foo" },
bp::std_out > merged, bp::posix::fd.bind(2, 1));
child.wait();
std::cout << "Program output was:" << std::endl;
for (std::string line; getline(merged, line);)
std::cout << "merged: " << std::quoted(line) << "\n";
Printing
Program output was:
merged: "Error1"
merged: "Hello World1"
merged: "Error2"
merged: "Error3"
merged: "Error4"
merged: "Error5"
merged: "Hello World2"
merged: "Error6"
merged: "Hello World3"
I'm trying to send a class over boost::message queue using boost::serialization, boost::Arcive, and boost::split members (load and save)
the problem is when I'm trying to deserialize I'm getting the input stream error exception
#include <iostream>
#include <boost/interprocess/shared_memory_object.hpp>
#include <boost/interprocess/ipc/message_queue.hpp>
#include <boost/version.hpp>
#include <random>
#include <boost/archive/text_oarchive.hpp>
#include <boost/archive/text_iarchive.hpp>
#include <boost/serialization/access.hpp>
#include <boost/archive/impl/basic_text_oarchive.ipp>
#include <boost/archive/impl/text_oarchive_impl.ipp>
#include <boost/archive/impl/text_iarchive_impl.ipp>
#include <boost/serialization/split_member.hpp>
class Data{
public:
int a_ ;
double b_ ;
std::string s ;
template<class Archive>
void serialize(Archive & ar, const unsigned int version) {
boost::serialization::split_member(ar, *this, version);
}
template<class Archive> void save(Archive & ar, unsigned int version) const {
// ar << order_request_type_;
ar << a_;
ar << b_;
ar << s; }
template<class Archive> void load(Archive & ar, unsigned int version) {
// ar >> order_request_type_;
ar >> a_;
ar >> b_;
ar >> s;
}
private:
// friend class boost::archive::access;
friend class boost::archive::save_access;
};
[[nodiscard]]bool RunChiled() {
using namespace boost::interprocess; try {
message_queue mq(open_only //open or create
, "message_queue" //name
);
unsigned int priority = 0;
message_queue::size_type recvd_size;
Data d;
std::stringstream iss;
std::string serialized_string;
serialized_string.resize(150);
long long number = 0;
while(true)
{
mq.receive(&serialized_string[0], 150, recvd_size , priority);
std::cout << serialized_string << "\n";
iss << serialized_string;
try{
boost::archive::text_iarchive ia(iss); // <-- getting the exception
ia >> d;
} catch (const std::exception& ex) {
std::cout << ex.what() << "\n";
}
number++;
std::cout << d.a_ << " " << d.b_ << " " << d.s << "\n";
} }catch(const interprocess_exception &ex) {
message_queue::remove("message_queue");
std::cout << "interprocess_exception " << ex.what() << std::endl;
return 1; } catch (const std::exception& e) {
std::cout << "exception " << e.what() << std::endl;
message_queue::remove("message_queue");
return 1; }
message_queue::remove("message_queue"); return true; }
int main() { std::cout << "1\n"; std::default_random_engine generator; std::uniform_real_distribution<double> distribution(0,15);
using namespace std;
cout << "Boost version: " << BOOST_LIB_VERSION << endl; using namespace boost::interprocess; message_queue::remove("message_queue"); auto pid = fork();
if(pid > 0) {
std::cout << "2\n";
sleep(2);
try {
auto res = RunChiled();
std::cout << res;
} catch (...) {
std::cout << "error\n";
}
} else if(pid == 0) {
try{
boost::interprocess::message_queue mq(create_only,"message_queue", 100, 150);
std::stringstream oss;
Data request;
request.b_ = 17.5;
request.a_ = I;
request.s = to_string(17.5) + " " + to_string(i);
try {
boost::archive::text_oarchive oa(oss);
oa << request;
} catch (const std::exception& e) {
std::cout << "serialzation:" << e.what() ;
}
try{
// std::cout << "oss " << oss.str() << "\n";
std::string serialized_string(oss.str());
std::cout << "serialized_string " << oss.str().size() << "\n";
mq.send(&serialized_string, serialized_string.size(), 0);
}catch(const std::exception& e){
std::cout << "\n send exeption " << e.what() << "\n";
}
}
}catch (const std::exception& e){
message_queue::remove("message_queue");
std::cout << e.what() ;
}
}
return 0;
}
A number of big issues.
Firstly
mq.send(&serialized_string, serialized_string.size(), 0);
That's Undefined
Behaviour because
serialzed_string isn't POD and the size doesn't match either. You
probbably meant something like on the receive side:
mq.send(serialized_string.data(), serialized_string.size(), 0);
You're resizing your serialized_string message to 150, and never
back to the actual size. This means it will not work correctly as
there will be trailing data.
Fixing it:
std::string buffer(buffer_size, '\0');
unsigned int priority = 0;
message_queue::size_type recvd_size;
mq.receive(buffer.data(), buffer.size(), recvd_size, priority);
buffer.resize(recvd_size);
Other Notes
The serialization can be simpler without splitting:
class Data {
public:
int a_;
double b_;
std::string s;
template <class Archive> void serialize(Archive& ar, unsigned) {
ar & a_ & b_ & s;
}
private:
friend class boost::serialization::access; // not required
};
Other notes
return 1 and return true from bool runChiled look iffy - one of them is probably a bug
[[nodiscard]] seems a little bit tricky on a function that normally will not return ([[noreturn]] might be more apt, optionally just passing exceptions out?)
you will want to seed your random generator with something actually random:
std::default_random_engine generator { std::random_device{}() };
Live Demo With Fixes
Live On Coliru
Live On Wandbox
#include <iostream>
#include <iomanip>
#include <boost/interprocess/shared_memory_object.hpp>
#include <boost/interprocess/ipc/message_queue.hpp>
#include <boost/date_time.hpp>
#include <boost/core/demangle.hpp>
#include <boost/version.hpp>
#include <random>
#include <thread> // this_thread
#include <boost/archive/text_oarchive.hpp>
#include <boost/archive/text_iarchive.hpp>
#include <boost/serialization/access.hpp>
#include <boost/serialization/split_member.hpp>
namespace {
namespace bip = boost::interprocess;
using bip::message_queue;
auto constexpr queuename = "message_queue";
auto constexpr max_queued = 5;
auto constexpr buffer_size = 150;
auto sleep_for = [](auto d) { std::this_thread::sleep_for(d); };
using namespace std::chrono_literals;
}
class Data {
public:
int a_{};
double b_{};
std::string s;
template <class Ar> void serialize(Ar& ar, unsigned /*unused*/) {
ar& a_& b_& s;
}
};
[[noreturn]] void RunChild()
{
std::cout << "2" << std::endl;
sleep_for(2s);
try {
message_queue mq(bip::open_only,queuename);
size_t number = 0;
for (std::string buffer(buffer_size, '\0');; buffer.resize(buffer_size)) {
{
unsigned int priority = 0;
message_queue::size_type recvd_size = 0;
#ifdef COLIRU
// make the process terminate for online compiler
{
using namespace boost::posix_time;
auto deadline = second_clock::universal_time() + seconds(3);
if (not mq.timed_receive(buffer.data(), buffer.size(),
recvd_size, priority, deadline))
{
throw std::runtime_error("no more messages");
}
}
#else
mq.receive(buffer.data(), buffer.size(), recvd_size, priority);
#endif
buffer.resize(recvd_size);
}
//std::cout << buffer << std::endl;
Data d;
try {
std::stringstream iss(buffer);
boost::archive::text_iarchive ia(iss);
ia >> d;
} catch (const std::exception& ex) {
std::cout << ex.what() << std::endl;
}
++number;
std::cout << "Received: " << d.a_ << " " << d.b_ << " "
<< std::quoted(d.s) << std::endl;
}
} catch (const std::exception& e) {
std::cout << boost::core::demangle(typeid(e).name()) << " " << e.what()
<< std::endl;
message_queue::remove(queuename);
throw; // re-raise
}
message_queue::remove(queuename);
}
static void RunParent()
{
std::cout << "1" << std::endl;
std::default_random_engine generator { std::random_device{}() };
std::uniform_real_distribution<double> distribution(0, 15);
message_queue mq(bip::create_only, queuename, max_queued, buffer_size);
for (auto i = 0; i < 10; ++i) {
auto value = distribution(generator);
Data const request {
i,
value,
std::to_string(value) + " " + std::to_string(i)
};
std::stringstream oss;
try {
boost::archive::text_oarchive oa(oss);
oa << request;
std::string buffer = std::move(oss).str();
std::cout << "Sending " << buffer.size() << " bytes" << std::endl;
mq.send(buffer.data(), buffer.size(), 0);
} catch (const std::exception& e) {
std::cout << "\nsend exeption " << e.what() << std::endl;
}
}
}
int main() {
std::cout << "Boost version: " << BOOST_LIB_VERSION << std::endl;
message_queue::remove(queuename);
try {
if (auto pid = ::fork(); pid > 0) {
RunChild();
} else if (pid == 0) {
RunParent();
}
} catch (const std::exception& e) {
std::cout << e.what() << std::endl;
message_queue::remove(queuename);
}
}
Prints
Boost version: 1_75
2
1
Sending 72 bytes
Sending 73 bytes
Sending 72 bytes
Sending 72 bytes
Sending 73 bytes
Sending 72 bytes
Sending 72 bytes
Received: 0 1.12642 "1.126420 0"
Received: 1 14.2474 "14.247412 1"
Received: 2 3.22163 "3.221631 2"
Sending 73 bytes
Sending 72 bytes
Received: 3 3.20471 "3.204709 3"
Sending 72 bytes
Received: 4 10.7838 "10.783761 4"
Received: 5 5.74063 "5.740629 5"
Received: 6 6.98008 "6.980078 6"
Received: 7 11.6643 "11.664257 7"
Received: 8 3.80561 "3.805614 8"
Received: 9 7.79641 "7.796408 9"
std::runtime_error no more messages
no more messages
I use Boost ASIO and I would like to separate header from body message.
Here is my opcode enum:
enum ServerOpcode : uint16_t{
SMSG_AUTH_CONNECTION_RESPONSE = 0x001,
SMSG_LOGIN_REQUEST_RESPONSE = 0x002,
SMSG_LOGIN_REQUEST_RESPONSE_TEST = 0x1A6,
};
Here is how I send message:
boost::asio::io_context io_context;
std::vector<tcp::socket> sockets;
for (int i = 0; i < connectionsNumber; ++i) {
try
{
sockets.emplace_back(io_context);
tcp::socket& s{sockets.back()};
tcp::resolver resolver(io_context);
boost::asio::connect(s, resolver.resolve( defaultIP,config.GetConfigValue("AuthServerPort", defaultPort)));
enum { body_length = 1024 };
enum { header_length = 8 };
enum { max_length = body_length + header_length};
char header_[header_length];
char body_[body_length];
char data_[header_length + body_length];
ServerOpcode opc;
opc = ServerOpcode::SMSG_LOGIN_REQUEST_RESPONSE_TEST;
std::string message = "I am testing here!!!";
snprintf(header_,header_length,"%x",opc);
strcpy(body_, message.c_str());
sprintf(data_, "%s %s", header_, body_);
size_t request_length = sizeof(data_)/sizeof(*data_);
std::cout << "Header: " << header_ << std::endl;
std::cout << "body: " << body_ << std::endl;
std::cout << "whole message is: " << data_ << std::endl;
std::cout << "size: " << request_length << std::endl;
std::cout << "max size: " << max_length << std::endl;
boost::asio::write(s, boost::asio::buffer(data_, request_length));
}
catch (std::exception& e)
{
std::cerr << "Exception: " << e.what() << "\n";
}
}
Here is how I read it on the server:
void Vibranium::Client::read_header() {
auto self(shared_from_this());
boost::asio::async_read(socket,
boost::asio::buffer(_packet.data_, _packet.header_length),
[this, self](boost::system::error_code ec, std::size_t /*length*/)
{
if (!ec)
{
std::cout << "Header: " << std::endl;
std::cout.write(_packet.data_, _packet.header_length);
std::cout << "\n";
//read_body();
}
else
{
std::cerr << "Invalid header sent!" << std::endl;
}
});
}
Packet.h:
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include "string"
#include "Protocol/ServerOpcode.h"
class Packet {
public:
Packet() : body_length_(0)
{
}
enum { max_body_length = 1024 };
enum { header_length = 8 };
char header_[header_length];
char body_[max_body_length];
char data_[header_length + max_body_length];
char * body();
void PreparePacket(ServerOpcode serverOpcode, std::string message);
std::size_t body_length() const;
private:
std::size_t body_length_;
};
#endif //VIBRANIUM_CORE_PACKET_H
However I see output as:
New Connection (ID: 4)
Header:
1a6 I am
That is not correct! Why Header: is showing 1a6 I am? Where is my mistake?
I am expecting it to be just 1a6. How can I fix it?
In this line after reading:
std::cout.write(_packet.data_, _packet.header_length);
header_length is 8 = length of '1a6 I am'
Your header length should be 3 (not 8), if you want to have '1a6' only.
This line: snprintf(header_,header_length,"%x",opc); should have a newline character at the end of it if you're going to print it. If that's the case, you should extend your buffers by one to allow for a newline. Or better yet, print the expected characters and specify the specific length whenever they need to be printed (as you're doing on the server):
std::cout << "Header: " << std::endl;
std::cout.write(_packet.data_, _packet.header_length);
std::cout << "\n";
As the code is written now, the output continues (in contiguous memory), until the next newline character is hit.
Additionally, I'd recommend using vector<uint_8> instead of arrays as they allow for bounds checking and allow for default moveable/copyable semantics for the Packet type. You may also consider using the boost::asio::buffer type.
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.
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.