A guide to getting started with boost::asio
9. A boost::asio network wrapper (TCP)
Now that we know the basics of using the boost::asio library and some simple TCP networking aspects, we can take a look at a network wrapper that takes care of the low level stuff. By using the wrapper, we can reuse it and always focus on the application logic rather than rewriting the network code ach time.
IMPORTANT NOTE: This code is purely for education purposes. Do not use it in production systems because there might be bugs. The code is designed to work a specific way and that might result in improper execution if you do not require such behavior. While I have tested and deployed the code for numerous projects, I am always finding small issues to fix.
In addition, the overhead of using such a network wrapper has to be taken into account. For example, there are tons of allocations behind the scenes from the vector and lists. In addition, the judicial use of shared_ptr along with boost::bind handlers does add quite a bit of overhead that might not be acceptable in some environments That is why this code is for educational use only!
The networking library attempts to provide a thread safe scalable wrapper for easily implementing client and server applications. Users will derive their custom class from the base Connection, Acceptor, or Hive classes as needed. The following examples will show the basics of using the wrapper.
The first example we will look at is setting up a server using the wrapper. In this case, it is pretty similar to our previous examples. It simply echoes out all traffic sent or received. This server is a simple echo server this time around though.
The code should be pretty straight forward. Since we are using the wrapper now, all of the details of the socket management are behind the scenes now so we can focus on application logic. In this example, we do not use any worker threads, but the same concepts apply as have been shown in previous examples. We now know what a server looks like, so let us take a look at a client.
This client simply sends a HTTP GET to Google and then dumps the output in hex format to the console. The theme of reusability further shows itself in this example as the code in this example is not too different than the code of the server. This means our client and server programs are not going to be radically different. That is a good thing as it makes life easier for future maintenance.
With our networking wrapper, we can see how a lot of work is simplified for us. There are a lot of design implications to this particular networking wrapper that should be noticed. First, for "servers", there is no concept of storing each connection into a container for easy referencing. This is done because to add all connections into a container, the end user must lock the container to synchronize access to it or implement an async method of adding and removing connections. In short, this is behavior the end user has to implement if they need it. Not all networking applications have to be aware of all connections present at a time (HTTP servers for example) so the wrapper goes for the most generic approach beneficial to all.
Next, all connection interactions are done through a unique strand. As we covered already, the strand object allows events to be executed serially. As a result, we do not have to explicitly lock the connection each time an event happens because no events will ever happen concurrently no matter how many worker threads we have. Care has to be taken though because if the user adds any custom methods, then they would have to implement their logic following the same design to keep the class thread safe.
Simple send/recv buffer logic is implemented through vectors and lists. This obviously has serious memory implications in the long run. However, anyone who has custom memory needs will have their own unique system to work with, so they would modify the code as needed anyways. For most simple applications, the provided system is good enough and cross platform (standard C++ library containers) so no efforts have been made to complicate that aspect.
Finally, the specific design of the wrapper is not for everyone. This is just one example of what is possible with the boost::asio library. I prefer and use this wrapper myself, so that is why I am sharing it. Feel free to come up with your own and customize it as you need! The important part is getting familiar with the boost::asio library.