For my Publisher/Subscriber pattern I want to use topics. So publish different messages on different topics. I already used topics in ZMQ with Python, but can not find how to use in C++.
Is it possible to use topics with zmqcpp, or do I have to use different ports?
My publisher is very simple, similar to this one: http://zguide.zeromq.org/cpp:durapub
Thanks
Here is an example of a pub-sub in C++ :
#include <thread>
#include <zmq.hpp>
#include <iostream>
#include <signal.h>
#include <unistd.h>
static int s_interrupted = 0;
static void s_signal_handler (int signal_value)
{
if(s_interrupted == 0)
{
std::cout << "sighandler" << std::endl;
s_interrupted = 1;
zmq::context_t context(1);
zmq::socket_t socket(context, ZMQ_PAIR);
socket.connect("ipc://killmebaby");
zmq::message_t msg;
memcpy(msg.data(),"0", 1);
socket.send(msg);
}
}
// Setup signal handler to quit the program
static void s_catch_signals (void)
{
struct sigaction action;
action.sa_handler = s_signal_handler;
action.sa_flags = 0;
sigemptyset (&action.sa_mask);
sigaction (SIGINT, &action, NULL);
sigaction (SIGTERM, &action, NULL);
}
const std::string TOPIC = "4567";
void startPublisher()
{
zmq::context_t zmq_context(1);
zmq::socket_t zmq_socket(zmq_context, ZMQ_PUB);
zmq_socket.bind("ipc://localsock");
usleep(100000); // Sending message too fast after connexion will result in dropped message
zmq::message_t msg(3);
zmq::message_t topic(4);
for(int i = 0; i < 10; i++) {
memcpy(topic.data(), TOPIC.data(), TOPIC.size()); // <= Change your topic message here
memcpy(msg.data(), "abc", 3);
try {
zmq_socket.send(topic, ZMQ_SNDMORE);
zmq_socket.send(msg);
} catch(zmq::error_t &e) {
std::cout << e.what() << std::endl;
}
msg.rebuild(3);
topic.rebuild(4);
usleep(1); // Temporisation between message; not necessary
}
}
void startSubscriber()
{
zmq::context_t zmq_context(1);
zmq::socket_t zmq_socket(zmq_context, ZMQ_SUB);
zmq_socket.connect("ipc://localsock");
zmq::socket_t killer_socket(zmq_context, ZMQ_PAIR); // This socket is used to terminate the loop on a signal
killer_socket.bind("ipc://killmebaby");
zmq_socket.setsockopt(ZMQ_SUBSCRIBE, TOPIC.c_str(), TOPIC.length()); // Subscribe to any topic you want here
zmq::pollitem_t items [] = {
{ zmq_socket, 0, ZMQ_POLLIN, 0 },
{ killer_socket, 0, ZMQ_POLLIN, 0 }
};
while(true)
{
int rc = 0;
zmq::message_t topic;
zmq::message_t msg;
zmq::poll (&items [0], 2, -1);
if (items [0].revents & ZMQ_POLLIN)
{
std::cout << "waiting on recv..." << std::endl;
rc = zmq_socket.recv(&topic, ZMQ_RCVMORE); // Works fine
rc = zmq_socket.recv(&msg) && rc;
if(rc > 0) // Do no print trace when recv return from timeout
{
std::cout << "topic:\"" << std::string(static_cast<char*>(topic.data()), topic.size()) << "\"" << std::endl;
std::cout << "msg:\"" << std::string(static_cast<char*>(msg.data()), msg.size()) << "\"" << std::endl;
}
}
else if (items [1].revents & ZMQ_POLLIN)
{
if(s_interrupted == 1)
{
std::cout << "break" << std::endl;
break;
}
}
}
}
int main() {
s_catch_signals ();
run = true;
std::thread t_sub(startSubscriber);
sleep(1); // Slow joiner in ZMQ PUB/SUB pattern
std::thread t_pub(startPublisher);
t_pub.join();
t_sub.join();
}
You can find many more example in the examples section of the github repository
Not sure about the C++ API but with the C API you can subscribe to topics with the ZMQ_SUBSCRIBE socket option. I suspect the C++ API has a similar function.
This simply filters on messages that start with the same text as the topic text. You can use Pub-Sub Message Envelopes for a more robust solution. I can image that the Python API hides these implementation details.
Related
I'm trying to download this link using an C++ apllication. But the it encountered exception:
terminate called after throwing an instance of 'boost::wrapexcept'
what(): body limit exceeded
This application has requested the Runtime to terminate it in an unusual way.
Please contact the application's support team for more information.
I found out the problem is in http::write.
What on earth is going wrong? Does that mean I was just trying to download the whole file into the memory? If yes, can I continue using ssl::stream anymore? Or there's any alternative?
#ifndef __kernel_entry
#define __kernel_entry
#endif
#include <boost/asio.hpp>
#include <windows.h>
#include <boost/asio/ssl.hpp>
#include <boost/beast.hpp>
#include <boost/beast/http.hpp>
#include <boost/process.hpp>
#include <boost/process/async.hpp>
#include <iomanip>
#include <iostream>
void handle_batch(std::vector<size_t> params) {
std::mutex s_mx;
if (!params.empty()) {
// emulate some work, because I'm lazy
auto sum = std::accumulate(begin(params), end(params), 0ull);
// then wait some 100..200ms
{
using namespace std::chrono_literals;
std::mt19937 prng(std::random_device{}());
std::this_thread::sleep_for(
std::uniform_real_distribution<>(100,200)(prng)*1ms);
}
// simple thread id (thread::id displays ugly)
auto tid = std::hash<std::thread::id>{}(std::this_thread::get_id()) % 100;
// report results to stdout
std::lock_guard lk(s_mx); // make sure the output doesn't intermix
std::cout
<< "Thread #" << std::setw(2) << std::setfill('0') << tid
<< " Batch n:" << params.size()
<< "\tRange [" << params.front() << ".." << params.back() << "]"
<< "\tSum:" << sum
<< std::endl;
}
}
namespace net = boost::asio;
namespace ssl = net::ssl;
namespace beast = boost::beast;
namespace http = beast::http;
namespace process = boost::process;
using boost::system::error_code;
using boost::system::system_error;
using net::ip::tcp;
using stream = ssl::stream<tcp::socket>;
auto ssl_context() {
ssl::context ctx{ssl::context::sslv23};
return ctx;
}
void connect_https(stream& s, std::string const& host, tcp::resolver::iterator eps) {
net::connect(s.lowest_layer(), eps);
s.lowest_layer().set_option(tcp::no_delay(true));//dealt here
if (!SSL_set_tlsext_host_name(s.native_handle(), host.c_str())) {
throw system_error{ { (int)::ERR_get_error(), net::error::get_ssl_category() } };
}
s.handshake(stream::handshake_type::client);
}
auto get_request(std::string const& host, std::string const& path) {
using namespace http;
using std::cerr;
request<string_body> req;
req.version(11);cerr<<"__ver";
req.method(verb::get);cerr<<"__met";
req.target("https://" + host + path);cerr<<"__tar";
//req.set(field::user_agent, "test");
req.set(field::host, host);cerr<<"__set";
std::cerr << req << std::endl;
Sleep(1000);
return req;
}
int main() {
net::io_context io; // main thread does all io
net::thread_pool pool(6); // worker threads
// outside for lifetime
http::response_parser<http::buffer_body> response_reader;
beast::flat_buffer lookahead; // for the response_reader
std::array<char,512> buf{0}; // for download content
auto ctx = ssl_context();
ssl::stream<tcp::socket> s(io, ctx);
{ // synchronously write request
std::string host = "www.sfml-dev.org";
try{
connect_https(s, host, tcp::resolver{io}.resolve(host, "https"));
}catch(std::exception& e){std::cerr<<e.what()<<std::endl;}
http::write(s, get_request(host, "/files/SFML-2.5.1-windows-gcc-7.3.0-mingw-32-bit.zip"));
http::read_header(s, lookahead, response_reader);
std::cerr << "Headers: " << response_reader.get().base() << std::endl;
}//https://www.sfml-dev.org/files/SFML-2.5.1-windows-gcc-7.3.0-mingw-32-bit.zip
// now, asynchoronusly read contents
process::async_pipe pipe_to_zcat(io);
std::function<void(error_code, size_t)> receive_zip;
receive_zip = [&s, &response_reader, &pipe_to_zcat, &buf, &lookahead, &receive_zip](error_code ec, size_t /*ignore_this*/) {
auto& res = response_reader.get();
auto& body = res.body();
if (body.data) {
auto n = sizeof(buf) - body.size;
net::write(pipe_to_zcat, net::buffer(buf, n));
}
bool done = ec && !(ec == http::error::need_buffer);
done += response_reader.is_done();
if (done) {
std::cerr << "receive_zip: " << ec.message() << std::endl;
pipe_to_zcat.close();
} else {
body.data = buf.data();
body.size = buf.size();
http::async_read(s, lookahead, response_reader, receive_zip);
}
};
// kick off receive loop
receive_zip(error_code{}, 0);
process::async_pipe zcat_output(io);
process::child zcat(
process::search_path("zcat"),
process::std_in < pipe_to_zcat,
process::std_out > zcat_output,
process::on_exit([](int exitcode, std::error_code ec) {
std::cerr << "Child process exited with " << exitcode << " (" << ec.message() << ")\n";
}), io);
std::function<void(error_code, size_t)> receive_primes;
net::streambuf sb;
receive_primes = [&zcat_output, &sb, &receive_primes, &pool](error_code ec, size_t /*transferred*/) {
{
std::istream is(&sb);
size_t n = std::count(net::buffers_begin(sb.data()), net::buffers_end(sb.data()), '\n');
std::vector<size_t> batch(n);
std::copy_n(std::istream_iterator<size_t>(is), n, batch.begin());
is.ignore(1, '\n'); // we know a newline is pending, eat it to keep invariant
post(pool, std::bind(handle_batch, std::move(batch)));
}
if (ec) {
std::cerr << "receive_primes: " << ec.message() << std::endl;
zcat_output.close();
} else {
net::async_read_until(zcat_output, sb, "\n", receive_primes);
}
};
// kick off handler loop as well:
receive_primes(error_code{}, 0);
io.run();
pool.join();
}
I am experimenting with ZMQ using a XPUB/XSUB proxy. Ultimately I want to insert some logic in the middle so I attempted to build my own proxy with zmq_poll() instead of using the built in zmq_proxy. However my XPUB never receives any subscription messages to forward. The code below works (subscriber prints "helloWorld" ) if I use the built in proxy. It does not work with my custom proxy. I've tried setting the very verbose/manual socket options and adding delays but can't seem to make it work:
Ex-post [Comment]:
after further testing I realized this was just a dumb copy and past bug and that the arguments to zmq::poll were not what I thought. the second argument is the number of items in the list. which should be 2 and not 1. – CrimsonKnights Dec 13 at 5:45
void proxyBuiltIn(zmq::context_t* context)
{
zmq::socket_t frontend(*context, ZMQ_XSUB);
zmq::socket_t backend(*context, ZMQ_XPUB);
frontend.bind("inproc://frontend");
backend.bind("inproc://backend");
zmq::proxy(frontend, backend);
}
void proxyCustom(zmq::context_t* context)
{
zmq::socket_t frontend(*context, ZMQ_XSUB);
zmq::socket_t backend(*context, ZMQ_XPUB);
frontend.bind("inproc://frontend");
backend.bind("inproc://backend");
zmq::pollitem_t items[2] =
{
{ frontend, 0, ZMQ_POLLIN, 0 },
{ backend, 0, ZMQ_POLLIN, 0 },
};
while (1)
{
if (zmq::poll(items, 1, 1000) == -1){ break;}
if (items[0].revents & ZMQ_POLLIN)
{
std::cout << "got published message" << std::endl; // won't get here because subscription is not made
zmq::multipart_t message;
message.recv(frontend);
message.send(backend);
}
if (items[1].revents & ZMQ_POLLIN)
{
std::cout << "got subscription message" << std::endl; // never gets here.
zmq::multipart_t message;
message.recv(backend);
message.send(frontend);
}
}
}
void subscriber(zmq::context_t* context)
{
zmq::socket_t subscriber(*context, ZMQ_SUB);
subscriber.connect("inproc://backend");
std::string topic = "testTopic";
subscriber.setsockopt(ZMQ_SUBSCRIBE, topic.c_str(), topic.size());
while (1)
{
zmq::multipart_t incoming;
incoming.recv(subscriber);
std::string topic = incoming.popstr();
std::string data = incoming.popstr();
std::cout << topic.c_str() << ", " << data.c_str() << std::endl;
}
}
void publisher(zmq::context_t* context)
{
zmq::socket_t publisher(*context, ZMQ_PUB);
publisher.connect("inproc://frontend");
while (1)
{
zmq::multipart_t message;
message.addstr("testTopic");
message.addstr("helloWorld!");
message.send(publisher);
std::this_thread::sleep_for(std::chrono::milliseconds(1000));
}
}
int main(int argc, char **argv)
{
zmq::context_t* context = new zmq::context_t(1);
std::thread(proxyCustom, context).detach(); // this does not
// std::thread(proxyBuiltIn, context).detach(); // this works
std::thread( publisher, context).detach();
std::thread(subscriber, context).detach();
while(1)
{
}
}
HINT: This works if I instantiate the io_context inside the for loop.
I know this code looks a little goofy, but it's a simplified version of code that's bigger and has this structure. Why can't I receive a second packet with the below code? It works fine with bool synch = true;. Here's the output I get:
iteration 0
receive udp
posted receive
got a packet
iteration 1
receive udp
posted receive
I have to hit Ctrl-c to quit. I expect to see "got a packet" a second time.
The receiver:
#include <array>
#include <iostream>
#include <functional>
#include <thread>
#include <boost/asio.hpp>
namespace asio = boost::asio;
namespace ip = asio::ip;
using ip::udp;
using std::cout;
using std::endl;
using boost_ec = boost::system::error_code;
int main() {
asio::io_context ioContext;
std::array<char, 65500> buffer;
auto asioBuffer = asio::buffer(buffer);
bool synch = false;
udp::endpoint remoteEndpoint;
for (unsigned int i = 0; i < 2; ++i) {
cout << "iteration " << i << endl;
auto recvSocket = udp::socket(ioContext,
udp::endpoint(udp::v4(), 9090));
if (synch) {
recvSocket.receive_from(asioBuffer, remoteEndpoint);
cout << "received a packet" << endl;
} else {
std::function<void(const boost_ec&, size_t)> impl =
[&](const boost_ec &, size_t packetSize) {
if (packetSize > 0) {
cout << "got a packet" << endl;
return;
}
cout << "receive udp" << endl;
recvSocket.async_receive_from(asioBuffer,
remoteEndpoint,
impl);
cout << "posted receive" << endl;
};
impl(boost_ec(), 0);
while (ioContext.poll() == 0) {
std::this_thread::sleep_for(std::chrono::milliseconds(200));
}
}
}
}
The sender:
#include <array>
#include <iostream>
#include <boost/asio.hpp>
namespace asio = boost::asio;
namespace ip = asio::ip;
namespace chrono = std::chrono;
using ip::udp;
using std::cout;
using std::endl;
int main() {
std::array<char, 65500> buffer;
asio::io_context ioContext;
auto socket = udp::socket(ioContext);
socket.open(udp::v4());
auto endpoint = udp::endpoint(udp::v4(), 9090);
size_t packetsSent = 0;
size_t bytesSent = 0;
const double APPROX_BYTES_PER_SEC = 1e6;
const auto CHECK_INTERVAL = chrono::microseconds(100);
auto beforeStart = chrono::steady_clock::now();
auto start = beforeStart;
size_t bytesSentSinceStart = 0;
while (true) {
auto now = chrono::steady_clock::now();
auto timePassed = now - start;
if (timePassed > CHECK_INTERVAL) {
auto expectedTime = chrono::duration<double>(bytesSentSinceStart /
APPROX_BYTES_PER_SEC);
if (expectedTime > timePassed) {
std::this_thread::sleep_for(expectedTime - timePassed);
}
start = chrono::steady_clock::now();
bytesSentSinceStart = 0;
}
bytesSent += socket.send_to(asio::buffer(buffer), endpoint);
bytesSentSinceStart += buffer.size();
++packetsSent;
}
return 0;
}
I think this is the key:
void restart();
This function must be called prior to any second or later set of invocations of the run(), run_one(), poll() or poll_one() functions when a previous invocation of these functions returned due to the io_context being stopped or running out of work.
So the above code needs to call restart after every poll that results in ioContext.stopped() being true, and it becomes true when the ioContext no longer has anything attached to it waiting to happen.
I am trying to build up a client to get data via a specific protocol from a server.
I know that my code is not the best - but at the moment I am still experimenting with the basic functions of Boost ASIO.
I want to implement an read from TCP-Function which blocks until a specific amount of bytes have been received.
My Problem:
When I call boost::asio::read or boost::asio::write i geht following error:
error C2039: 'read_some' : is not a member of boost::shared_ptr'
I am working with VS2013 Professional, Boost 1.55.00 (precompiled).
Here is my Code: ( You can find the line by the comment "//HEEERE"
boost::mutex cout_lock;
int main()
{
// creating io_service
boost::shared_ptr<boost::asio::io_service> io_service(new boost::asio::io_service);
// creating work and assigning it to io_service
boost::shared_ptr<boost::asio::io_service::work> work(new boost::asio::io_service::work(*io_service));
// creating strand and assigning it to io_service
boost::shared_ptr<boost::asio::io_service::strand> strand(new boost::asio::io_service::strand(*io_service));
// creating socket
boost::shared_ptr<boost::asio::ip::tcp::socket> socket(new boost::asio::ip::tcp::socket(*io_service));
try {
// creating resolver
boost::asio::ip::tcp::resolver resolver(*io_service);
// creating query
boost::asio::ip::tcp::resolver::query query(IPConfig_str, boost::lexical_cast<std::string>(IPConfig_PortNr));
// creating iterator
boost::asio::ip::tcp::resolver::iterator iterator = resolver.resolve(query);
// creating endpoint
boost::asio::ip::tcp::endpoint endpoint = *iterator;
// connecting synchronously
socket->connect(endpoint);
}
catch(std::exception &ex) {
cout_lock.lock();
std::cout << "[main]:\t" << "Exception:" << ex.what() << std::endl;
cout_lock.unlock();
}
// Create Query
CommandCreator CMDCreator;
Command sendCommand;
CMDCreator.Create_fpga_GetSwVers(&sendCommand);
std::cout << std::endl;
std::cout << "SENT:" << std::endl;
for (int i = 0; i < sendCommand.length; i++)
{
std::cout << std::hex << std::setw(2) << std::setfill('0') << int(sendCommand.buffer[i]) << ", ";
}
std::cout << std::endl;
// Send Query
boost::system::error_code ec;
socket->async_send(boost::asio::buffer(sendCommand.buffer, sendCommand.length), boost::asio::transfer_all());
Sleep(300); // sleep 100 ms (at least 85 <- not stable!)
// Receive Answer - Header
Command receiveCommandHeader;
receiveCommandHeader.InitBuffer(4);
// Async
// socket->async_receive(boost::asio::buffer(receiveCommandHeader.buffer, receiveCommandHeader.length), 0, boost::bind(HandleRead, ec));
//HEEERE
boost::asio::read(socket, boost::asio::buffer(receiveCommandHeader.buffer, receiveCommandHeader.length), boost::asio::transfer_all(), ec);
//shutting down
socket->shutdown(boost::asio::ip::tcp::socket::shutdown_both, ec);
socket->close(ec);
io_service->stop();
return 0;
}
class Command
{
friend class CommandCreator; // TODO: is there a better and as simple method as a friend class?
public:
Command() : buffer(0)
{}
virtual ~Command()
{
delete[] buffer;
buffer = 0;
}
void InitBuffer(int const len)
{
this->length = len;
this->buffer = new uint8_t[len];
}
uint8_t* buffer;
int length;
};
Actually the problem is located at this part of boost in the file read.hpp, where async_read_some is called from 'stream_'.
void operator()(const boost::system::error_code& ec,
std::size_t bytes_transferred, int start = 0)
{
std::size_t n = 0;
switch (start_ = start)
{
case 1:
n = this->check_for_completion(ec, total_transferred_);
for (;;)
{
stream_.async_read_some(
boost::asio::buffer(buffer_ + total_transferred_, n),
BOOST_ASIO_MOVE_CAST(read_op)(*this));
return; default:
total_transferred_ += bytes_transferred;
if ((!ec && bytes_transferred == 0)
|| (n = this->check_for_completion(ec, total_transferred_)) == 0
|| total_transferred_ == boost::asio::buffer_size(buffer_))
break;
}
handler_(ec, static_cast<const std::size_t&>(total_transferred_));
}
}
Okey, I've just found the problem.
// creating socket
boost::shared_ptr<boost::asio::ip::tcp::socket> socket(new boost::asio::ip::tcp::socket(*io_service));
I created the socket as a pointer but all the interfaces of read, read_some and other boost-library functions require the object. Therefore adding the dereferencing operator did it:
boost::asio::async_read(*socket, boost::asio::buffer(receiveCommandHeader.buffer, receiveCommandHeader.length),
boost::asio::transfer_all(), boost::bind(HandleRead, ec));
First i asked this Running a function on the main thread from a boost thread and passing parameters to that function
so now i am trying this:
The following is a console c++ project where i perfectly simulated my big project
TestServicePost.cpp
#include "stdafx.h"
#include "SomeClass.h"
int _tmain(int argc, _TCHAR* argv[])
{
SomeClass* s = new SomeClass();
while(true)
{
s->update();
}
return 0;
}
SomeClass.h
#include <boost/thread.hpp>
#include <boost/asio.hpp>
#include <queue>
class ServiceNote
{
public:
std::string getType()
{
std::stringstream typeSS;
typeSS << "LamasaTech.MultiWall.PostNote." << (NoteType.compare("Normal") == 0 ? "Node" : "Header") << "." << Shape << "." << Colour;
return typeSS.str();
}
int Action;
int CNoteId;
std::string Colour;
int NoteId;
std::string NoteType;
int SessionId;
std::string Shape;
std::string Style;
std::string Text;
int X;
int Y;
};
class SomeClass
{
public:
SomeClass();
~SomeClass();
void update();
private:
std::queue<ServiceNote> pendingNotes;
void addToQueue(ServiceNote sn);
void pollService(boost::asio::io_service* svc);
int getMessage(boost::asio::io_service* svc, std::string sessionId, int messageId);
boost::thread servicePoller;
};
SomeClass.cpp
#include "stdafx.h"
#include "SomeClass.h"
#include <boost/property_tree/ptree.hpp>
#include <boost/property_tree/json_parser.hpp>
#include <boost/asio/signal_set.hpp>
#define POLL_SERVICE = 0;
#define POLLING_WAIT_TIME 1000
#define SAVE_SESSION_EVERY 1800000
SomeClass::SomeClass()
{
boost::asio::io_service io_servicePoller;
io_servicePoller.run();
servicePoller = boost::thread(boost::bind(&SomeClass::pollService, this, &io_servicePoller));
/*boost::asio::io_service io_sessionSaver;
boost::asio::signal_set signalsSaver(io_sessionSaver, SIGINT, SIGTERM);
signalsSaver.async_wait( boost::bind(&boost::asio::io_service::stop, &io_sessionSaver));
sessionSaver = boost::thread(&SomeClass::saveSessionEvery, io_sessionSaver);*/
}
SomeClass::~SomeClass()
{
}
void SomeClass::update()
{
while(!pendingNotes.empty())
{
ServiceNote sn = pendingNotes.front();
pendingNotes.pop();
}
}
void SomeClass::addToQueue(ServiceNote sn)
{
pendingNotes.push(sn);
}
void SomeClass::pollService(boost::asio::io_service* svc)
{
int messageId = 1;
while(true)
{
if(boost::this_thread::interruption_enabled() && boost::this_thread::interruption_requested())
return;
int currentId = messageId;
messageId = getMessage(svc, "49", messageId);
if(currentId == messageId)
boost::this_thread::sleep(boost::posix_time::milliseconds(POLLING_WAIT_TIME));
}
}
int SomeClass::getMessage(boost::asio::io_service* svc, std::string sessionId, int messageId)
{
try
{
boost::asio::io_service io_service;
// Get a list of endpoints corresponding to the server name.
boost::asio::ip::tcp::resolver resolver(io_service);
boost::asio::ip::tcp::resolver::query query("mw.rombus.com", "http");
boost::asio::ip::tcp::resolver::iterator endpoint_iterator = resolver.resolve(query);
// Try each endpoint until we successfully establish a connection.
boost::asio::ip::tcp::socket socket(io_service);
boost::asio::connect(socket, endpoint_iterator);
// Form the request. We specify the "Connection: close" header so that the
// server will close the socket after transmitting the response. This will
// allow us to treat all data up until the EOF as the content.
boost::asio::streambuf request;
std::ostream request_stream(&request);
request_stream << "GET " "/Service.svc/message/" << sessionId << "/" << messageId << " HTTP/1.0\r\n";
request_stream << "Host: " << "mw.rombus.com" << "\r\n";
request_stream << "Accept: */*\r\n";
request_stream << "Connection: close\r\n\r\n";
// Send the request.
boost::asio::write(socket, request);
// Read the response status line. The response streambuf will automatically
// grow to accommodate the entire line. The growth may be limited by passing
// a maximum size to the streambuf constructor.
boost::asio::streambuf response;
boost::asio::read_until(socket, response, "\r\n");
// Check that response is OK.
std::istream response_stream(&response);
std::string http_version;
response_stream >> http_version;
unsigned int status_code;
response_stream >> status_code;
std::string status_message;
std::getline(response_stream, status_message);
if (!response_stream || http_version.substr(0, 5) != "HTTP/")
{
//std::cout << "Invalid response\n";
return messageId;
}
if (status_code != 200)
{
//std::cout << "Response returned with status code " << status_code << "\n";
return messageId;
}
// Read the response headers, which are terminated by a blank line.
boost::asio::read_until(socket, response, "\r\n\r\n");
// Process the response headers.
std::string header;
std::string fullHeader = "";
while (std::getline(response_stream, header) && header != "\r")
fullHeader.append(header).append("\n");
// Write whatever content we already have to output.
std::string fullResponse = "";
if (response.size() > 0)
{
std::stringstream ss;
ss << &response;
fullResponse = ss.str();
try
{
boost::property_tree::ptree pt;
boost::property_tree::read_json(ss, pt);
ServiceNote sn;
sn.Action = pt.get<int>("Action");
sn.CNoteId = pt.get<int>("CNoteId");
sn.Colour = pt.get<std::string>("Colour");
sn.NoteId = pt.get<int>("NoteId");
sn.NoteType = pt.get<std::string>("NoteType");
sn.SessionId = pt.get<int>("SessionId");
sn.Shape = pt.get<std::string>("Shape");
sn.Style = pt.get<std::string>("Style");
sn.Text = pt.get<std::string>("Text");
sn.X = pt.get<int>("X");
sn.Y = pt.get<int>("Y");
svc->post(boost::bind(&SomeClass::addToQueue, this, sn));
//pendingNotes.push(sn);
}
catch (std::exception const& e)
{
std::string test = e.what();
//std::cerr << e.what() << std::endl;
}
messageId++;
}
// Read until EOF, writing data to output as we go.
std::string fullSth = "";
boost::system::error_code error;
while (boost::asio::read(socket, response,
boost::asio::transfer_at_least(1), error))
{
std::ostringstream ss;
ss << &response;
fullSth = ss.str();
}
if (error != boost::asio::error::eof)
throw boost::system::system_error(error);
}
catch (std::exception& e)
{
std::string test = e.what();
std::cout << "Exception: " << e.what() << "\n";
}
return messageId;
}
but i get Unhandled exception at 0x771215de in TestServicePost.exe: 0xC0000005: Access violation writing location 0xcccccce4., right after this line executes:
svc->post(boost::bind(&SomeClass::addToQueue, this, sn));
I couldn't define io_service as a class member so i can use it in the destructor ~SomeClass(), would appreciate help on that too
If io_service.post is not the best solution for me please recommend something, as you can see i have a constructor, destructor and an update method who is called every tick, i tried using this and the queue alone but it wasn't thread safe, is there an easy thread safe FIFO to use ?
In SomeClass constructor you actually do the following:
Define a local io_service instance.
Call its run() member-function, which returns immediately, because io_service has no work.
Pass an address of the local object to another thread.
This certainly won't work.
Note that io_service::run() is a kind of "message loop", so it should block the calling thread. Don't call it in object constructor.
I figured out how to declare io_service as a class member:
boost::shared_ptr< boost::asio::io_service > io_servicePoller;
and in the constructor i did the following:
SomeClass::SomeClass()
{
boost::shared_ptr< boost::asio::io_service > io_service(
new boost::asio::io_service
);
io_servicePoller = io_service;
servicePoller = boost::thread(boost::bind(&SomeClass::pollService, this, io_servicePoller));
}
Some cleanup
SomeClass::~SomeClass()
{
servicePoller.interrupt();
io_servicePoller->stop();
servicePoller.join();
}
and in update i called run which adds the stuff into the queue, then reads them in the while loop
void SomeClass::update()
{
io_servicePoller->run();
io_servicePoller->reset();
while(!pendingNotes.empty())
{
ServiceNote sn = pendingNotes.front();
pendingNotes.pop();
}
}
and changed my members signature to void SomeClass::pollService(boost::shared_ptr< boost::asio::io_service > svc)
So what happens is:
The app starts
inits my class
my class makes a service and starts the thread
the thread fetches items from the service
the main thread checks the io service queue and exuted it
then it uses the queue
Thanks to Igor R. i couldn't have done it without him
and also http://www.gamedev.net/blog/950/entry-2249317-a-guide-to-getting-started-with-boostasio?pg=4 where i got how to make the shared pointer