Thread: Boost.Asio's io_service and event handling

  1. #1
    Registered User Chris87's Avatar
    Join Date
    Dec 2007
    Posts
    139

    Boost.Asio's io_service and event handling

    I'm trying to figure out how to use Boost.Asio's network functionality so that I can make some server software. I'm learning how to do this by examining the C++11 chat server example found here and I seem to understand most of it. However, assuming io_service.run() is the catalyst for event loops, I'm not seeing anywhere how it yields a notification such as "oh, the client sent some data" or an event handler of any sort. What's more, it seems a lot like in the chat_session class, it defines what the client does with the data received from the server. I'm not writing a client program as well, only the server part. I should be able to code the server in such a way that it sends the client(s) data and let whoever writes the client manage what to do with it on their end, but I'm not seeing that in this example.

    I did this before successfully in Qt, but I've since been understanding Boost a lot better and wanted to try and write a less dependency-cluttered version. I know Boost has a signals library as well, but I wouldn't know how to entwine it with the Asio library in this case.
    Last edited by Chris87; 02-01-2016 at 03:25 AM.

  2. #2
    Registered User
    Join Date
    Oct 2006
    Posts
    3,445
    Have you looked at the TCP Server Example that Boost provides on their website? It does a pretty good job of explaining things.
    What can this strange device be?
    When I touch it, it gives forth a sound
    It's got wires that vibrate and give music
    What can this thing be that I found?

  3. #3
    Registered User Chris87's Avatar
    Join Date
    Dec 2007
    Posts
    139
    Hmm... I don't see where it explains any event loops. In Qt, there were signals (ie. dataReceived) and slots (ie. handleDataReceived) and I understood "this signal would fire when something specific happens and the slot would execute the action connected to it", but here it sounds very linear (do this, this, this, this, end). If there is an event mechanism here, it's very obscure to me, even in the tutorial you linked.

  4. #4
    Registered User
    Join Date
    Oct 2006
    Posts
    3,445
    Boost doesn't work the same way as Qt. You don't have control over any event loops. You start operations, and the io_service handles all events on those operations. You don't hook up signals and slots like in Qt. For asynchronous operations, you can call async_read and async_write, and pass a boost::bind object that contains the callback and parameter placeholders, just like in the example I posted. My suggestion would be to try it, and when you have problems, come back here, post your code in a new thread, and ask specific questions about what is going on in your situation.
    What can this strange device be?
    When I touch it, it gives forth a sound
    It's got wires that vibrate and give music
    What can this thing be that I found?

  5. #5
    Registered User Chris87's Avatar
    Join Date
    Dec 2007
    Posts
    139
    Well I'll be happy to post the code (at least what matters as far as Boost.Asio needs). I'm implementing a server for an old visual avatar chat thing called Palace. The PDF I have of its protocol specifies a binary protocol where the server and client send and receive message packets from each other.

    The format of these messages are as follows:
    event type: uint32 or a four-byte ASCII string describing the opcode
    length: uint32, Length of the message's body, minus the 12 bytes the header's made up of
    refnum: sint32, usually used as a variable relative to the message type (if it was "assign ID to the client logging in", this field would be that ID number)
    body: uint8 array[length] for the message body, if it has one

    Below I'll post what code I have (the important stuff anyway) along with some added comments, #includes are implied here, so yes, I did include deque/vector/string etc

    Code:
    class Server final
    {
    public:
    	Server(boost::asio::io_service&, const tcp::endpoint&);
    private:
    	void Listen();
    	void SendID(Connection*); // sends the client their assigned user ID
    	void ReadLogin(Connection*); // client will respond with login data message
    	void SendLoginReply(Connection*); // basically echoes client's login message for integrity
    	
    	std::deque<Message> msg_queue;
    	std::vector<Connection*> users;
    	std::vector<Room> rooms;
    	tcp::acceptor listener;
    	tcp::socket socket;
    	// various metadata fields omitted due to irrelevancy
    };
    
    // represents a connection between server and client
    class Connection final: 
    {
    public:
    	Connection(tcp::socket&);
    	inline tcp::socket& Socket() const { return socket; }
    	// get/set method madness for misc fields omitted
    	void Disconnect();
    private:
    	tcp::socket socket;
    	// other stuff omitted
    };
    
    // a chatroom, more or less
    class Room final
    {
    public:
    	Room(std::uint16_t); // param is the room ID
    	void Join(Connection*);
    	void Leave(Connection*);
    private:
    	// various metadata fields omitted
    	std::uint16_t id;
    };
    
    struct Message final
    {
    	char *data;
    	std::uint32_t type;
    	std::uint32_t size;
    	std::int32_t refnum;
    	
    	const char* SerialiseHeader() const; // inserts above 3 fields into a char array of 12 bytes for buffering
    };
    
    Server::Server(boost::asio::io_service &iosvc, const tcp::endpoint &ep):
    	listener(iosvc, ep),
    	socket(iosvc),
    	last_user_id(0)
    {
    	Listen();
    }
    
    void Server::Listen()
    {
    	listener.async_accept(socket,
    		[this](boost::system::error_code ec)->void
    		{
    			if (!ec)
    			{
    				auto c = new Connection(std::move(socket));
    				SendID(c);
    				users.push_back(c);
    			}
    			
    			Listen();
    		});
    }
    
    void Server::SendID(Connection *c)
    {
    	Message msg = { nullptr, MSG_TIYID, 0, ++last_user_id };
    	char *data = msg.SerialiseHeader();
    	
    	boost::asio::async_write(c->Socket(), boost::asio::buffer(data, 12),
    		[c](boost::system::error_code ec)->void
    		{
    			if (ec)
    				c->Disconnect();
    		});
    }
    After Server::SendID(), ReadLogin() should be executed if the client sends its login info, but I am unsure how to chronologically do that. I was planning to have the server utilise async_read() inside a method with a switch case depending what the client sends, but am unsure how to go about that.

  6. #6
    Frequently Quite Prolix dwks's Avatar
    Join Date
    Apr 2005
    Location
    Canada
    Posts
    8,057
    Typically each function would execute async_read or async_write with the next function to be called registered as the callback. They daisy-chain together. In case of errors the error handling callback is used instead. I don't know how well this scales but it's fine for medium sized programs.

  7. #7
    Registered User Chris87's Avatar
    Join Date
    Dec 2007
    Posts
    139
    Hmm... so how would I order the execution based on if the client responds or not?

  8. #8
    Frequently Quite Prolix dwks's Avatar
    Join Date
    Apr 2005
    Location
    Canada
    Posts
    8,057
    You could register a function on a timer to handle the case where the client doesn't respond: c++ - boost asio deadline_timer - Stack Overflow

    Note that if the underlying TCP connection is interrupted, asio should notice that automatically. You only have to worry about (semi-malicious?) clients which keep the TCP connection open but don't send anything.

  9. #9
    Registered User Chris87's Avatar
    Join Date
    Dec 2007
    Posts
    139
    I just don't know exactly what async does in this case? If I call async_read(), will Boost wait until data is available, or try to read it immediately upon call?

  10. #10
    Frequently Quite Prolix dwks's Avatar
    Join Date
    Apr 2005
    Location
    Canada
    Posts
    8,057
    The asynchronous functions wait until the operation completes (data ready to read, or socket reports write has been processed) before calling the callback function. That's the point of asynchronous I/O. You say, I know this read/write is going to happen, but I don't know when, so please let me know when it does so I can get onto (register) the next operation.

  11. #11
    Registered User Chris87's Avatar
    Join Date
    Dec 2007
    Posts
    139
    Oh okay. Daisy chaining and recursion makes sense then. Now can this execute multiple times using a thread pool or something because otherwise it would seem like it would block other incoming connections
    Last edited by Chris87; 02-05-2016 at 09:37 PM.

  12. #12
    Frequently Quite Prolix dwks's Avatar
    Join Date
    Apr 2005
    Location
    Canada
    Posts
    8,057
    You can use multiple threads each with an io_service, but asio multiplexes all asynchronous requests that you make and waits for any of them to complete. It basically adds all the file descriptors to a select/epoll/etc set and waits for one of them to be ready. So you can safely put as many simultaneous connections into one thread as you like, until you reach the bandwidth of a single thread, which is really pretty high. Basically asio is designed to replace the "one thread per connection" with a single thread using the efficient but complex model that POSIX operating systems provide with select, which some networking libraries sweep under the rug.

  13. #13
    Registered User Chris87's Avatar
    Join Date
    Dec 2007
    Posts
    139
    Now I see why Boost is a good thing to keep under your pillow for any programmer. Thanks! I think I'm good to go now.

Popular pages Recent additions subscribe to a feed

Similar Threads

  1. Converting From Boost::Asio::streambuf to a string
    By EverydayDiesel in forum C++ Programming
    Replies: 2
    Last Post: 01-07-2016, 11:26 AM
  2. Replies: 2
    Last Post: 04-14-2013, 12:25 PM
  3. Boost Asio and asynchronous I/O
    By Elysia in forum C++ Programming
    Replies: 9
    Last Post: 06-19-2011, 07:51 PM
  4. Open a serial port asynchronously with boost asio?
    By TriKri in forum C++ Programming
    Replies: 2
    Last Post: 05-16-2010, 12:33 PM
  5. Boost ASIO
    By PetrolMan in forum C++ Programming
    Replies: 0
    Last Post: 04-10-2009, 03:24 PM