c++ - How to prevent ASIO based server from terminating -
i have been reading boost asio tutorials. far, understanding entire send , receive loop can iterated once. please have @ following simple code:
client.cpp:
#include <boost/asio.hpp> #include <boost/array.hpp> #include <iostream> #include <string> boost::asio::io_service io_service; boost::asio::ip::tcp::resolver resolver(io_service); boost::asio::ip::tcp::socket sock(io_service); boost::array<char, 4096> buffer; void read_handler(const boost::system::error_code &ec, std::size_t bytes_transferred) { if (!ec) { std::cout << std::string(buffer.data(), bytes_transferred) << std::endl; sock.async_read_some(boost::asio::buffer(buffer), read_handler); } } void connect_handler(const boost::system::error_code &ec) { if (!ec) { sock.async_read_some(boost::asio::buffer(buffer), read_handler); } } void resolve_handler(const boost::system::error_code &ec, boost::asio::ip::tcp::resolver::iterator it) { if (!ec) { sock.async_connect(*it, connect_handler); } } int main() { boost::asio::ip::tcp::resolver::query query("localhost", "2013"); resolver.async_resolve(query, resolve_handler); io_service.run(); }
the program resolves
address, connects
server , reads
data, , ends
when there no data. question: how can continue loop? mean, how can keep connection between client , server during entire lifetime of application server sends data whenever has send? tried break circle seams trapped inside io_service.run()
same question holds in case of sever also:
server.cpp :
#include <boost/asio.hpp> #include <string> boost::asio::io_service io_service; boost::asio::ip::tcp::endpoint endpoint(boost::asio::ip::tcp::v4(), 2013); boost::asio::ip::tcp::acceptor acceptor(io_service, endpoint); boost::asio::ip::tcp::socket sock(io_service); std::string data = "hello, world!"; void write_handler(const boost::system::error_code &ec, std::size_t bytes_transferred) { } void accept_handler(const boost::system::error_code &ec) { if (!ec) { boost::asio::async_write(sock, boost::asio::buffer(data), write_handler); } } int main() { acceptor.listen(); acceptor.async_accept(sock, accept_handler); io_service.run(); }
this example. in real application, may keep socket open , reuse other data exchanges(both read , write). how may that.
i value kind comments. if have references easy solutions addressing issue, appreciate if mention it. thank you
update (server sample code)
based on answer given below(update 2), wrote server code. please note code simplified (able compile&run though). note io_service never return coz waiting new connection. and how io_service.run
never returns , runs ever. whenever want io_service.run return, make acceptor not accept anymore. please in 1 of many ways don't remember.(seriously, how in clean way? :) )
enjoy:
#include <boost/asio.hpp> #include <boost/thread.hpp> #include <string> #include <iostream> #include <vector> #include <time.h> boost::asio::io_service io_service; boost::asio::ip::tcp::endpoint endpoint(boost::asio::ip::tcp::v4(), 2013); boost::asio::ip::tcp::acceptor acceptor(io_service, endpoint); //boost::asio::ip::tcp::socket sock(io_service); std::string data = "hello, world!"; class observer; std::vector<observer*> observers; class observer { public: observer(boost::asio::ip::tcp::socket *socket_):socket_obs(socket_){} void notify(std::string data) { std::cout << "notify called data[" << data << "]" << std::endl; boost::asio::async_write(*socket_obs, boost::asio::buffer(data) , boost::bind(&observer::write_handler, this,boost::asio::placeholders::error)); } void write_handler(const boost::system::error_code &ec) { if (!ec) //no error: done, wait next notification return; socket_obs->close(); //client error , exit read_handler observers.erase(std::find(observers.begin(), observers.end(),this)); std::cout << "observer::write_handler returns nothing written" << std::endl; } private: boost::asio::ip::tcp::socket *socket_obs; }; class server { public: void creatsocketandaccept() { socket_ = new boost::asio::ip::tcp::socket(io_service); observers.push_back(new observer(socket_)); acceptor.async_accept(*socket_,boost::bind(&server::handle_accept, this,boost::asio::placeholders::error)); } server(boost::asio::io_service& io_service) { acceptor.listen(); creatsocketandaccept(); } void handle_accept(const boost::system::error_code& e) { creatsocketandaccept(); } private: boost::asio::ip::tcp::socket *socket_; }; class agent { public: void update(std::string data) { if(!observers.empty()) { // std::cout << "calling notify data[" << data << "]" << std::endl; observers[0]->notify(data); } } }; agent agent; void agentsim() { int = 0; sleep(10);//wait me start client while(i++ < 10) { std::ostringstream out(""); out << data << ; // std::cout << "calling update data[" << out.str() << "]" << std::endl; agent.update(out.str()); sleep(1); } } void run() { io_service.run(); std::cout << "io_service returned" << std::endl; } int main() { server server_(io_service); boost::thread thread_1(agentsim); boost::thread thread_2(run); thread_2.join(); thread_1.join(); }
you can simplify logic of asio based porgrams follows: each function calls async_x function provides handler. bit transitions between states of state machine, handlers states , async-calls transitions between states. exiting handler without calling async_* function transition end state. program "does" (sending data, receiving data, connecting sockets etc.) occurs during transitions.
if see way, client looks (only "good path", i.e. without errors):
<start> --(resolve)----> resolve_handler resolve_handler --(connect)----> connect_handler connect_handler --(read data)--> read_handler read_handler --(read data)--> read_handler
your server loks this:
<start> --(accept)-----> accept handler accept_handler --(write data)-> write_handler write_handler ---------------> <end>
since write_handler
not anything, makes transition end state, meaning ioservice::run
returns. question is, want do, after data has been written socket? depending on that, have define corresponding transition, i.e. async-call want do.
update: comment see want wait next data ready i.e. next tick. transitions this:
write_handler --(wait tick/data)--> dataready dataready --(write data)----------> write_handler
you see, introduces new state (handler), called dataready
, call tick_handler
or else. transition write_handler easy:
void dataready() { // new data... async_write(sock, buffer(data), write_handler); }
the transition write_handler
can simple async_wait
on timer. if data come "outside" , don't know when ready, wait time, check if data there, , if not, wait more time:
write_handler --(wait time)--> checkfordata checkfordata:no --(wait time)--> checkfordata checkfordata:yes --(write data)------> write_handler
or, in (pseudo)code:
void write_handler(const error_code &ec, size_t bytes_transferred) { //... async_wait(ticklenght, checkfordata); } void checkfordata(/*insert wait handler signature here*/) { if (dataisready()) { async_write(sock, buffer(data), write_handler); } else { async_wait(shorttime, checkfordata): } }
update 2: according comment, have agent time management somehow (calling update every tick). here's how solve that:
- let agent have list of observers notified when there new data in update call.
- let each observer handle 1 client connection (socket).
- let server wait incomming connections, create observers them , register them agent.
i not firm in exact syntax of asio, handwavy pseudocode:
server:
void server::accept_handler() { obs = new observer(socket); agent.register(obs); new socket; //observer takes care of old 1 async_accept(..., accept_handler); }
agent:
void agent::update() { if (newdataavailable()) { (auto& obs : observers) { obs->notify(data); } } }
observer:
void observer::notify(data) { async_write(sock, data, write_handler); } void observer::write_handler(error_code ec, ...) { if (!ec) //no error: done, wait next notification return; //on error: close connection , unregister agent.unregister(this); socket.close(); //client error , exit read_handler }
Comments
Post a Comment