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)
{
}
}
Related
I want to initiate the TLS connection only if the server supports the secure connection, to achieve this i have introduced the two message type between the server and client.
Client will send SECURECONNREQ msg to server after tcp connection establishment, if the server is configured to support the TLS it send SECURECONNRESP.
On receiving SECURECONNRESP the client will initiate the the TLS handshake by calling boost asynhandshake API but this API is not sending the correct packet(client hello). In the wireshark i could see it is sending the protocol version as TLS1.1 even if it configured for TLS1.2.
Note: SSL context object is prepared with TLS1.2, ciphers and related certs.
It looks like the asynhandshake will not work properly if there is some data exchange happens on the TCP link.
Is there extra step we need take on BOOST asio to make it work?
The secure connection establishment works perfectly fine with below implementation:
Initiating the TLS connection without sending any data on the TCP link.
The client will initiate the TLS after connection is established(no data transfered on this link) by calling asynhandshake, the server will call boost asynhandshake immediately after the connnection accept.
It's hard, or even impossible, to tell without seeing your actual code. But I thought it a nice challenge to write the quintessential PLAIN/TLS server/client in Asio, just to see whether there were any problems I might not have anticipated.
Sadly, it works. You may compare these and decide what you're doing differently.
If anything, this should help you create a MCVE/SSCCE to repro your issue.
Short Intro
The server accepts connections.
The client initiates a ConnectionRequest. Depending on a runtime flag
_support_tls the server responds with the SECURE capability response (or not).
The client and server upgrade to TLS accordingly.
The client and server execute a very simple protocol session that's
templated on the Stream type, because the implementations are independent of
the transport layer security.
Note, for the example I've used the server.pem from the Asio SSL examples.
The demo then executes servers both with and without SECURE capability, and
verifies that the same simple_client implementation works correctly
against both.
Listing
#include <boost/asio.hpp>
#include <boost/asio/ssl.hpp>
#include <boost/endian/arithmetic.hpp>
#include <boost/beast.hpp>
#include <boost/beast/http.hpp>
#include <iostream>
namespace http = boost::beast::http;
namespace net = boost::asio;
namespace ssl = net::ssl;
using boost::system::error_code;
using net::ip::tcp;
struct ConnectionRequest {
enum { MAGIC = 0x64fc };
boost::endian::big_uint16_t magic = MAGIC;
};
struct ConnectionResponse {
enum Capabilities {
NONE,
SECURE,
};
boost::endian::big_uint16_t capabilities = NONE;
};
using TLSStream = ssl::stream<tcp::socket>;
template <typename> static inline char const* stream_type = "(plain)";
template <> inline char const* stream_type<TLSStream> = "(TLS)";
static void do_shutdown(tcp::socket& s) {
error_code ignored;
s.shutdown(tcp::socket::shutdown_both, ignored);
}
static void do_shutdown(TLSStream& s) {
error_code ignored;
s.shutdown(ignored);
}
template <typename Stream>
struct Session : std::enable_shared_from_this<Session<Stream> > {
Session(Stream&& s) : _stream(std::move(s)) {}
void start() {
std::cout << "start" << type() << ": " << _stream.lowest_layer().remote_endpoint() << std::endl;
http::async_read(_stream, _buf, _req, self_bind(&Session::on_request));
}
private:
Stream _stream;
http::request<http::string_body> _req;
http::response<http::empty_body> _res;
net::streambuf _buf;
void on_request(error_code ec, size_t) {
std::cout << "on_request" << type() << ": " << ec.message() << std::endl;
http::async_write(_stream, _res, self_bind(&Session::on_response));
}
void on_response(error_code ec, size_t) {
std::cout << "on_reponse" << type() << ": " << ec.message() << std::endl;
do_shutdown(_stream);
}
auto type() const { return stream_type<Stream>; }
auto self_bind(auto member) {
return [self=this->shared_from_this(), member](auto&&... args) {
return (*self.*member)(std::forward<decltype(args)>(args)...);
};
}
};
struct Server {
using Ctx = ssl::context;
Server(auto executor, uint16_t port, bool support_tls)
: _acceptor(executor, {{}, port}),
_support_tls(support_tls)
{
if (_support_tls) {
_ctx.set_password_callback(
[](size_t, Ctx::password_purpose) { return "test"; });
_ctx.use_certificate_file("server.pem", Ctx::file_format::pem);
_ctx.use_private_key_file("server.pem", Ctx::file_format::pem);
}
_acceptor.listen();
accept_loop();
}
~Server() {
_acceptor.cancel();
}
private:
void accept_loop() {
_acceptor.async_accept(
make_strand(_acceptor.get_executor()),
[=, this](error_code ec, tcp::socket&& sock) {
if (!ec)
accept_loop();
else
return;
// TODO not async for brevity of sample here
ConnectionRequest req[1];
read(sock, net::buffer(req));
if (req->magic != ConnectionRequest::MAGIC) {
std::cerr << "Invalid ConnectionRequest" << std::endl;
return;
}
ConnectionResponse res[1]{{ConnectionResponse::NONE}};
if (not _support_tls) {
write(sock, net::buffer(res));
std::make_shared<Session<tcp::socket>>(std::move(sock))
->start();
} else {
res->capabilities = ConnectionResponse::SECURE;
write(sock, net::buffer(res));
// and then do the handshake
TLSStream stream(std::move(sock), _ctx);
stream.handshake(TLSStream::handshake_type::server);
std::make_shared<Session<TLSStream>>(std::move(stream))
->start();
}
});
}
tcp::acceptor _acceptor;
Ctx _ctx{Ctx::method::sslv23};
bool _support_tls;
};
// simplistic one-time request/response convo
template <typename Stream> void simple_conversation(Stream& stream) {
http::write(stream, http::request<http::string_body>(
http::verb::get, "/demo/api/v1/ping", 11,
"hello world"));
{
http::response<http::string_body> res;
net::streambuf buf;
http::read(stream, buf, res);
std::cout << "Received: " << res << std::endl;
}
}
void simple_client(auto executor, uint16_t port) {
// client also not async for brevity
tcp::socket sock(executor);
sock.connect({{}, port});
ConnectionRequest req[1];
write(sock, net::buffer(req));
ConnectionResponse res[1];
read(sock, net::buffer(res));
std::cout << "ConnectionResponse: " << res->capabilities << std::endl;
if (res->capabilities != ConnectionResponse::SECURE) {
simple_conversation(sock);
} else {
std::cout << "Server supports TLS, upgrading" << std::endl;
ssl::context ctx{ssl::context::method::sslv23};
TLSStream stream(std::move(sock), ctx);
stream.handshake(TLSStream::handshake_type::client);
simple_conversation(stream);
}
}
int main() {
net::thread_pool ctx;
{
Server plain(ctx.get_executor(), 7878, false);
simple_client(ctx.get_executor(), 7878);
}
{
Server tls(ctx.get_executor(), 7879, true);
simple_client(ctx.get_executor(), 7879);
}
ctx.join();
}
Prints
ConnectionResponse: start(plain): 0
127.0.0.1:37924
on_request(plain): Success
on_reponse(plain): Success
Received: HTTP/1.1 200 OK
ConnectionResponse: 1
Server supports TLS, upgrading
start(TLS): 127.0.0.1:36562
on_request(TLS): Success
on_reponse(TLS): Success
Received: HTTP/1.1 200 OK
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.
I want to know when dispatchhas finished with some specific work
service.dispatch(&some_work);
I want to know this because I need to restart some_work if it has finished.
struct work
{
std::shared_ptr<asio::io_service> io_service;
bool ready;
std::mutex m;
template <class F>
void do_some_work(F&& f)
{
if (io_service && ready) {
m.lock();
ready = false;
m.unlock();
io_service->dispatch([&f, this]() {
f();
m.lock();
ready = true;
m.unlock();
});
}
}
work(std::shared_ptr<asio::io_service> io_service)
: io_service(io_service)
, ready(true)
{
}
};
int
main()
{
auto service = std::make_shared<asio::io_service>();
auto w = std::make_shared<asio::io_service::work>(*service);
std::thread t1([&] { service->run(); });
work some_work{ service };
for (;;) {
some_work.do_some_work([] {
std::cout << "Start long draw on thread: " << std::this_thread::get_id()
<< std::endl;
std::this_thread::sleep_for(std::chrono::seconds(5));
std::cout << "End long draw on thread: " << std::this_thread::get_id()
<< std::endl;
});
}
w.reset();
t1.join();
}
There are some problems with the code, for example if some_workgoes out of scope, then the running taskwould still write to ready.
I am wondering if something like this already exists in Asio?
For lifetime issues, the common idiom is indeed to use shared pointers, examples:
Ensure no new wait is accepted by boost::deadline_timer unless previous wait is expired
Boost::Asio Async write failed
Other than that, the completion handler is already that event. So you would do:
void my_async_loop() {
auto This = shared_from_this();
socket_.async_read(buffer(m_buffer, ...,
[=,This](error_code ec, size_t transferred) {
if (!ec) {
// do something
my_async_loop();
}
}
);
}
This will re-schedule an (other?) async operation once the previous has completed.
On the subject of threadsafety, see Why do I need strand per connection when using boost::asio?
I'm trying to run an example of websocket++ that consists in receive messages from websocket clients and broadcast to all connected clients, but i having problems with thread synchronization.
In the code example the method process_messages waits for message on a std:queue
boost::unique_lock<boost::mutex> lock(m_action_lock);
while(m_actions.empty()) {
m_action_cond.wait(lock);
}
And the on_message handler locks the queue before to push a new message received from client, but when it try to notify_one(), the program fail with an Segmentation fault 11.
void on_message(connection_hdl hdl, server::message_ptr msg) {
// queue message up for sending by processing thread
{
boost::unique_lock<boost::mutex> lock(m_action_lock);
m_actions.push(action(MESSAGE,msg));
lock.unlock();
}
m_action_cond.notify_one();
}
The only way that the program works is commenting the wait(lock) but i not sure if this is safe.
Some body could help me to find de segmentation fault cause?
The complete code is:
#include <websocketpp/config/asio_no_tls.hpp>
#include <websocketpp/server.hpp>
#include <iostream>
#include <boost/thread.hpp>
#include <boost/thread/mutex.hpp>
#include <boost/thread/condition_variable.hpp>
typedef websocketpp::server<websocketpp::config::asio> server;
using websocketpp::connection_hdl;
using websocketpp::lib::placeholders::_1;
using websocketpp::lib::placeholders::_2;
using websocketpp::lib::bind;
/* on_open insert connection_hdl into channel
* on_close remove connection_hdl from channel
* on_message queue send to all channels
*/
enum action_type {
SUBSCRIBE,
UNSUBSCRIBE,
MESSAGE
};
struct action {
action(action_type t, connection_hdl h) : type(t), hdl(h) {}
action(action_type t, server::message_ptr m) : type(t), msg(m) {}
action_type type;
websocketpp::connection_hdl hdl;
server::message_ptr msg;
};
class broadcast_server {
public:
broadcast_server() {
// Initialize Asio Transport
m_server.init_asio();
// Register handler callbacks
m_server.set_open_handler(bind(&broadcast_server::on_open,this,::_1));
m_server.set_close_handler(bind(&broadcast_server::on_close,this,::_1));
m_server.set_message_handler(bind(&broadcast_server::on_message,this,::_1,::_2));
}
void run(uint16_t port) {
// listen on specified port
m_server.listen(port);
// Start the server accept loop
m_server.start_accept();
// Start the ASIO io_service run loop
try {
m_server.run();
} catch (const std::exception & e) {
std::cout << e.what() << std::endl;
} catch (websocketpp::lib::error_code e) {
std::cout << e.message() << std::endl;
} catch (...) {
std::cout << "other exception" << std::endl;
}
}
void on_open(connection_hdl hdl) {
boost::unique_lock<boost::mutex> lock(m_action_lock);
//std::cout << "on_open" << std::endl;
m_actions.push(action(SUBSCRIBE,hdl));
lock.unlock();
m_action_cond.notify_one();
}
void on_close(connection_hdl hdl) {
boost::unique_lock<boost::mutex> lock(m_action_lock);
//std::cout << "on_close" << std::endl;
m_actions.push(action(UNSUBSCRIBE,hdl));
lock.unlock();
m_action_cond.notify_one();
}
void on_message(connection_hdl hdl, server::message_ptr msg) {
// queue message up for sending by processing thread
boost::unique_lock<boost::mutex> lock(m_action_lock);
//std::cout << "on_message" << std::endl;
m_actions.push(action(MESSAGE,msg));
lock.unlock();
m_action_cond.notify_one();
}
void process_messages() {
while(1) {
boost::unique_lock<boost::mutex> lock(m_action_lock);
while(m_actions.empty()) {
m_action_cond.wait(lock);
}
action a = m_actions.front();
m_actions.pop();
lock.unlock();
if (a.type == SUBSCRIBE) {
boost::unique_lock<boost::mutex> lock(m_connection_lock);
m_connections.insert(a.hdl);
} else if (a.type == UNSUBSCRIBE) {
boost::unique_lock<boost::mutex> lock(m_connection_lock);
m_connections.erase(a.hdl);
} else if (a.type == MESSAGE) {
boost::unique_lock<boost::mutex> lock(m_connection_lock);
con_list::iterator it;
for (it = m_connections.begin(); it != m_connections.end(); ++it) {
m_server.send(*it,a.msg);
}
} else {
// undefined.
}
}
}
private:
typedef std::set<connection_hdl,std::owner_less<connection_hdl>> con_list;
server m_server;
con_list m_connections;
std::queue<action> m_actions;
boost::mutex m_action_lock;
boost::mutex m_connection_lock;
boost::condition_variable m_action_cond;
};
int main() {
broadcast_server server;
// Start a thread to run the processing loop
boost::thread(bind(&broadcast_server::process_messages,&server));
// Run the asio loop with the main thread
server.run(9002);
}
I can reproduce this behavior when Boost is compiled using g++ and libstdc++ but the program linking to it is compiled using clang and libc++. The libstdc++ and libc++ standard libraries are not ABI compatible, so you will need to build everything with one or everything with the other.
Details on how to compile Boost in C++11 mode with clang/libc++:
How to compile/link Boost with clang++/libc++?
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