Thread: [Cppcms-users] "endless" stream of data
Brought to you by:
artyom-beilis
From: Marcel H. <ke...@co...> - 2011-12-27 13:42:17
|
Hi guys, I'd like to use some new, cool, trendy, hightech, modern and ultracool technique called SSE or ServerSentEvents (https://developer.mozilla.org/en/Server-sent_events/Using_server-sent_events) But to do that I'd like to make an endless stream of data like the PHP script does. But how to "flush" the data to the client? I tried response().finalize() context().async_complete_response() (but i can't use it, because the object isn't useable for communication anymore) and some other tricks. So I want to now, how to make an asynchron, endless stream of data in cppcms. Maybe you can add an example for this, because it's HTML5, and that rocks ;) Regards Marcel Hellwig |
From: Artyom B. <art...@ya...> - 2011-12-27 13:57:03
|
Take a look on a function: cppcms::http::context::async_flush_output http://art-blog.no-ip.info/cppcms_ref_v0_99/classcppcms_1_1http_1_1context.html#a134469b5dc46667596c360b8b7284dff Small notes: 1. Don't use finalize, it is not needed any more. 2. async_complete_response() - completes the response, and you don't want to. Take a look on the chat example, but instead of calling async_complete_response() you call async_flush_output() with a callback. Note: you need to release the context the same way it is done in chat example. Artyom Beilis -------------- CppCMS - C++ Web Framework: http://cppcms.sf.net/ CppDB - C++ SQL Connectivity: http://cppcms.sf.net/sql/cppdb/ >________________________________ > From: Marcel Hellwig <ke...@co...> >To: cpp...@li... >Sent: Tuesday, December 27, 2011 3:42 PM >Subject: [Cppcms-users] "endless" stream of data > > >Hi guys, > >I'd like to use some new, cool, trendy, hightech, modern and ultracool technique called SSE or ServerSentEvents (https://developer.mozilla.org/en/Server-sent_events/Using_server-sent_events) > >But to do that I'd like to make an endless stream of data like the PHP script does. But how to "flush" the data to the client? >I tried > >response().finalize() >>context().async_complete_response() (but i can't use it, because the object isn't useable for communication anymore) >> >> and some other tricks. So I want to now, how to make an asynchron, endless stream of data in cppcms. Maybe you can add an example for this, because it's HTML5, and that rocks ;) > >Regards > >Marcel Hellwig > >------------------------------------------------------------------------------ >Write once. Port to many. >Get the SDK and tools to simplify cross-platform app development. Create >new or port existing apps to sell to consumers worldwide. Explore the >Intel AppUpSM program developer opportunity. appdeveloper.intel.com/join >http://p.sf.net/sfu/intel-appdev >_______________________________________________ >Cppcms-users mailing list >Cpp...@li... >https://lists.sourceforge.net/lists/listinfo/cppcms-users > > > |
From: Marcel H. <ke...@co...> - 2011-12-27 14:16:03
|
could you give me a simple example? Just writing something stupid. I don't get the async flush output method. Don't know what the handler is and does. |
From: Artyom B. <art...@ya...> - 2011-12-27 14:52:00
|
Please, read carefully the chat example and see how callbacks are used. It is all about event handling. Remember the you run inside event loop. and operations are done via callbacks. It is not like in PHP where "comet" event holds entire thread. Read 3 articles under "Concepts" there: http://art-blog.no-ip.info/wikipp/en/page/cppcms_1x You probably looking for something like this. myservice : public cppcms::application { void main(std::string) { context_=release_context(); // now we can handle new connection - note it is very simplistic // we store only one connection, there may be more } void handle_some_external_event() { context_->out() << "event!" << std::endl; context_->async_flush_output(boost::bind(&my_service::on_complete,this,_1); } void on_complete(completion_type c) { if(c!=operation_completed) { // error - disconnect context_.reset(); // delete context } // done we can wait for next event ... } booster::shared_ptr<cppcms::http::context> context_; }; Artyom Beilis -------------- CppCMS - C++ Web Framework: http://cppcms.sf.net/ CppDB - C++ SQL Connectivity: http://cppcms.sf.net/sql/cppdb/ >________________________________ > From: Marcel Hellwig <ke...@co...> >To: Artyom Beilis <art...@ya...>; cpp...@li... >Sent: Tuesday, December 27, 2011 4:15 PM >Subject: Re: [Cppcms-users] "endless" stream of data > >could you give me a simple example? Just writing something stupid. I don't get the async flush output method. Don't know what the handler is and does. > > > |
From: Marcel H. <ke...@co...> - 2011-12-27 19:34:22
|
Alright, my code is a bit different from yours and from the chat example but else it doesn't work. void SSE::loop(std::string /*page*/) { response().io_mode(cppcms::http::response::asynchronous); response().set_header("Content-Type", "text/event-stream"); int counter = (rand() % 10) + 1; while (true) { response().out() << "event: ping\n" << "data: {\"time\": \"" << "foo" //note: this is just an example << "\"}\n\n"; counter--; if (!counter) { response().out() << "data: is a message at time " << "bar" << "\n\n"; counter = (rand() % 10) + 1; } context().async_flush_output(boost::bind(&SSE::handler, this, -1)); booster::ptime::sleep(booster::ptime::seconds(1)); } } void SSE::handler(int status) { std::cout << "Status: " << status << std::endl; } two major questions. You told me to use release_context but if I do so, the connection "aborts" (access to unassigned context) while the second loop on response().out() How can I figure out when the user "leaves" the page, so I can stop producing tons of messages? Every connection should get it's own messagestream (it's user dependent). |
From: Artyom B. <art...@ya...> - 2011-12-27 21:34:23
|
>________________________________ > From: Marcel Hellwig <ke...@co...> >To: Artyom Beilis <art...@ya...>; cpp...@li... >Sent: Tuesday, December 27, 2011 9:34 PM >Subject: Re: [Cppcms-users] "endless" stream of data > >Alright, my code is a bit different from yours and from the chat example but else it doesn't work. > >void SSE::loop(std::string /*page*/) >{ > response().io_mode(cppcms::http::response::asynchronous); Don't need this... As if the application is mounted as asynchronous (make sure you did) then it is the default mode. > response().set_header("Content-Type", "text/event-stream"); > > int counter = (rand() % 10) + 1; General notes below You write the code as it is synchronous code, it is not! You don't work like this. > > while (true) { > response().out() << "event: ping\n" ><< "data: {\"time\": \"" ><< "foo" //note: this is just an example ><< "\"}\n\n"; > counter--; > > if (!counter) { > response().out() << "data: is a message at time " ><< "bar" << "\n\n"; > counter = (rand() % 10) + 1; > } > > context().async_flush_output(boost::bind(&SSE::handler, this, -1)); You should use a handler per call, Once you called async flush you should not do further operation till you receive completion notifications > booster::ptime::sleep(booster::ptime::seconds(1)); You block there entire event loop, you need to use booster::aio::deadline_timer and handle events asynchronously... > > } >} > >void SSE::handler(int status) >{ > std::cout << "Status: " << status << std::endl; >} > >two major questions. You told me to use release_context but if I do so, the connection "aborts" (access to unassigned context) while the second loop on response().out() You release it and you become responsible for the context life time, you need to keep it withing your class as long as you need. >How can I figure out when the user "leaves" the page, so I can stop producing tons of messages? > Two ways: a) The completion handler of async_flush_output returns a error status b) You can using async_on_peer_reset callback. Take a look in the chat example in the cppcms sources get(std::string no) { ... else if(pos == messages_.size()) { // Get the context <------------------------------------------- booster::shared_ptr<cppcms::http::context> context=release_context(); // Keep it <-------------------------------------------- waiters_.insert(context); // Handle disconnect Asynchronously!!! context->async_on_peer_reset( bind( &chat::remove_context, booster::intrusive_ptr<chat>(this), context)); } ... } Now take a look on function broadcast: void broadcast() { for(waiters_type::iterator waiter=waiters_.begin();waiter!=waiters_.end();++waiter) { (*waiter)->response().set_plain_text_header(); (*waiter)->response().out() << messages_.back(); // n (*waiter)->async_complete_response(); } waiters_.clear(); } Lets change the line (*waiter)->async_complete_response(); To (*waiter)->async_flush_output( boost::bind(&chat::new_handle, booster::intrusive_ptr<chat>(this), _1, *waiter, messages_.size())); And add new handler for "endless handling": void new_handle(cppcms::http::context::completion_type t, booster::shared_ptr<cppcms::http::context> context, size_t broadcasted) { if(t) return; // non-zero error if(broadcasted == messages_.size()) // no new messages mark for waiting once agains waiters_.push_back(context); else { // send all we got meanwhile while(broadcasted < messages_.size()) { context->response().out() << messages_[broadcaster] <<'\n; broadcasted++; } // send message asychronously context->async_flush_output( boost::bind( &chat::new_handle, booster::intrusive_ptr<chat>(this), _1, context, messages_.size())); } } And you got chat with "endless" stream, of course it would not work with the given XMLHttpRequest example but you get the general idea. BOTTOM LINE ----------- Think asynchronously... Regards, Artyom |