Example 3
This tutorial explores more about session and server classes, and it also shows how to set attributes of remote pointer. It introduces the items below:
- Handling session and server events
- Connect session and server with customized bindings.
- Setting number of thread for global io_runner
- Binding multiple instances of object as remote services
- Binding target as remote service through a session object at client site
- Change remote pointer to one-way call mode
- Rethrowing exception pointer
We will make both server and client applications export remote interface. Server will export 2 objects as remote player and 1 object as remote air conditions service. Client will export info_service
so that the server can query the name of the connected session. Let's start by defining 3 remote interfaces below which are to be included to both client and server applications.
Note: only the remote interface definition part of code is shown.
// remote_player.hpp REMOTE_CLASS_BEGIN(remote_player) \ REMOTE_METHOD_M1(void, play, std::string) \ REMOTE_METHOD_M0(void, stop) \ REMOTE_CLASS_END
// remote_ac.hpp REMOTE_CLASS_BEGIN(remote_ac) \ REMOTE_METHOD_M1(void, set_temperature, int) \ REMOTE_METHOD_C0(int, get_temperature) \ REMOTE_CLASS_END
// info_service.hpp REMOTE_CLASS_BEGIN(info_service) \ REMOTE_METHOD_C0(std::string, name) \ REMOTE_CLASS_END
Then define the service handler classes for server.
// model.hpp class audio_player { public: void play(std::string song) { m_playing = true; } void stop() { m_playing = false; } private: bool m_playing; }; class air_cond { public: void set_temperature(int t) { m_temperature = t; } int get_temperature() const { return m_temperature; } private: int m_temperature; };
And the service handler class for client.
// session_info.hpp class session_info { public: session_info(std::string name): m_name(name) {} std::string name() const { return m_name; } private: std::string m_name; };
In this example, the server application code will be more complicated. We will create on_start
function to handle the server start event and on_accept
function to handle the session accepted event. The on_start
function will handle the error by rethrowing the exception held by exception_ptr in a try-catch block. The on_accept
function will call the what
method of the exception_ptr to get the error message directly and if there is no error, it will get the info_service
remote pointer from the connected session to query the session name.
In the main function, we will set the global io runner to run in 2 threads and then start the server with customized binding.
// server.cpp #include <remote/server.hpp> #include <remote/global.hpp> #include <remote/make_basic_binding.hpp> #include <remote/bindings/binary_serializer.hpp> #include <remote/bindings/ipc_transport.hpp> void on_start(remote::exception_ptr e) { if(!e) { std::cout << "server started" << std::endl; return; } // handle error by rethrowing the exception try { remote::rethrow_exception(e); } catch(remote::system_error& error) { std::cout << "system error: " << error.what() << std::endl; } catch(remote::remote_error& error) { std::cout << "remote error: " << error.what() << std::endl; } catch(std::exception& error) { std::cout << "other error: " << error.what() << std::endl; } } void on_accept(remote::exception_ptr e, remote::session& session) { // another way to handle error is to call the e->what() directly if(e) { std::cout << "error when accepting a session: " << e->what() << std::endl; return; } try { // get info service interface from the newly connected session. auto info = session.get<info_service>("info"); std::cout << "accepted a session: " << info->name() << std::endl; } catch(std::exception& error) { std::cout << "error when get session info: " << error.what() << std::endl; } } int main() { // set application io runner to run 2 threads remote::global::io_runner(2); // create target objects audio_player walkman; audio_player ipod; air_cond room_ac; // create multiple instances of remote services // by binding target objects to their remote interfaces remote::server server; server.bind<remote_player>(&walkman, "walkman"); server.bind<remote_player>(&ipod, "ipod"); server.bind<remote_ac>(&room_ac, "room ac"); // construct customized binding typedef remote::bindings::binary_serializer serializer; typedef remote::bindings::ipc_transport transport; auto binding = remote::make_basic_binding<serializer, transport>("tutorial"); // setting event handlers and start the server server.set_accept_handler(on_accept); server.start(binding, on_start); std::cin.get(); return 0; }
// client.cpp #include <remote/session.hpp> #include <remote/attribute.hpp> #include <remote/global.hpp> #include <remote/make_basic_binding.hpp> #include <remote/bindings/binary_serializer.hpp> #include <remote/bindings/ipc_transport.hpp> void on_start(remote::exception_ptr e) { // handle session start event here } void on_stop(remote::exception_ptr e) { // handle session stop event here } void on_error(remote::exception_ptr e) { // handle io error event here } int main() { // set application io runner to run 2 threads remote::global::io_runner(2); // bind info service to session remote::session session; session_info info("My Session"); session.bind<info_service>(&info , "info"); // construct customized binding typedef remote::bindings::binary_serializer serializer; typedef remote::bindings::ipc_transport transport; auto binding = remote::make_basic_binding<serializer, transport>("tutorial"); // set event handler and start the session session.set_error_handler(on_error); session.start(binding, on_start); if(session.wait_for_ready() != remote::session::started) return -1; // get remote pointer auto ac = session.get<remote_ac>("room ac"); auto ipod = session.get<remote_player>("ipod"); auto walkman = session.get<remote_player>("walkman"); // use ac ac->set_temperature(25); // use walkman walkman->play("jazz"); // this is 2 way call // switch to one way mode remote::attribute::set_one_way(ipod, true); ipod->play("pop rock"); // this is one way call // switch back to normal mode remote::attribute::set_one_way(ipod, false); ipod->stop(); // this is 2 way call // stop the session and set event handler session.stop(on_stop); return 0; }Next Example