C Fdopen for Both Reading and Writing Sockets
- Download source code - ii.1 KB
Introduction
Socket programming is nothing of a new concept for programmers. Ever since the internet came into existence, it shifted the prototype to net-enabled applications. That's where network programming models starts to announced. A socket is fundamentally the most basic technology of this network programming. Permit alone the technology for the moment, in almost cases, it's literally just a client-server model working. Server is supposed to serve the information requested or the required services past the client. The post-obit analogy will assist you understand the model.
Only how does that transfer of information have identify? Well, that involves networking services from ship layer frequently referred to as TCP/IP (Transport Control Protocol/Internet Protocol). For example, when you open your browser and search for something, yous're only requesting a server for some information over HTTP (non to forget HTTP is nothing but an awarding using TCP/IP service). Only where are the sockets? Allow me refer you back to the line where I said sockets are the "base" and and then they provide the programming interface for these protocols to work on. More often than not speaking, sockets are providing a way for 2 processes or programs to communicate over the network. Sockets provide sufficiency and transparency while causing almost no communication overhead.
Source
Why C++ though? Equally I mentioned earlier, sockets are merely providing an interface for network programming and accept nothing to do with programming language used for implementation. C++ might be the best selection in this regard because of the speed and efficiency information technology brings to the tabular array. Some might non agree with me at this statement considering of unsaid complexity by the language including but not restricted to transmission memory direction, template syntax, library incompatibility, compiler, etc. But I think differently. C++ volition let yous dig deep and run across the insights of what actually is going on at the lower level although expert concept and knowledge in computer networks is not debatable. This article will help you in giving a soft start with socket programming in C++ using heave library. But before digging into the code, let's clarify a few points a bit more than.
What Socket Programming is All About?
Let'due south talk most what a socket actually is and how it plays its role in the communication.
Socket is merely one endpoint of a two-fashion communication link. It represents a unmarried connection between two entities that are trying to communicate over the network most of the time which are server and customer. More than than two entities tin likewise exist set to communicate but by using multiple sockets.
This socket-based advice is done over the network; 1 cease of which could exist your calculator while other could exist at the other finish of the world (because again the browsing example) or over the same car (local host). At present the question arises how the server knows that a client is requesting for connection and also which service is being requested? This all is the game of IP address and port number. Every calculator has a specific IP accost which will be used to identify information technology. (If you're accessing a website, so the proper name volition somewhen be translated into IP address.) Which service, is distinguished past port number.
At present to sum it all upward when a customer is requesting a server for services, it opens a socket and passes the request to the server by specifying its IP address and port number (to permit server know which service is meant to be provided.) The server will accept the connection request and transfer the data or provide any other service requested. In one case request is served, the connectedness will be closed. Observe the workflow in the following diagram.
Why Heave.Asio?
Writing networking code that is portable is like shooting fish in a barrel to maintain has been an result since long. C++ took a step to resolve this issue by introducing boost.asio
. It is a cross-platform C++ library for network and low-level I/O programming that provides developers with a consistent asynchronous model using a mod C++ arroyo. Hither's a list of what it offers:
- Cantankerous platform networking code (code would work on Windows, Linux, etc.)
- IPv4 and IPv6 support
- Asynchronous event support
- Timer back up
- iostream compatibility
And much more. You lot can become the complete overview of the library hither.
We are not going to dig deep into networking but rather will develop a uncomplicated client-server model and run across how things piece of work. So without whatsoever further delay, let'south get started.
Environment Setup
I'm currently in Linux (18.04 LTS) and then would exist roofing surroundings setup for the same.
Nosotros just need the following to get started:
-
heave.asio
- C++ compiler (preferably grand++)
- Text-editor
The simplest way to get asio on linux is past executing the following command:
$ sudo apt-get install libboost-all-dev
If you're using another platform or the above doesn't seem a good fit for you lot, follow the document here to become asio on your arrangement.
The next footstep is to make sure you have C++ compiler on your compiler. I'grand using grand++. You can become the aforementioned with the following command in linux.
$ sudo apt-become install g++
Once you accept the compiler, yous're skillful to follow along. I don't have whatever text-editor preference. Yous tin choose i of your pick.
Now that we have everything, nosotros are in a position to start coding for our TCP server-client model.
TCP Server
As nosotros mentioned before in the article, the server specifies an address for customer at which it makes a request to server. Server listens for the new connectedness and responds accordingly. Now here are the steps for our server evolution.
Of form, we need to import our libraries before annihilation else. So hither nosotros become:
#include < iostream > #include < boost/asio.hpp > using namespace boost::asio; using ip::tcp; using std::cord; using std::cout; using std::endl;
using namespace std
is considered a bad practice for the reason that it imports all sorts of names globally and can cause ambiguities. As we just need iii names that vest to std
namespace, it's better to import them separately or else adapt yourself.
We want our server to receive a bulletin from customer and so respond dorsum. For that, nosotros need two functions for read and write.
string read_(tcp::socket & socket) { boost::asio::streambuf buf; boost::asio::read_until( socket, buf, " \n" ); string data = boost::asio::buffer_cast< const char*>(buf.data()); return information; } void send_(tcp::socket & socket, const string& bulletin) { const string msg = message + " \due north"; boost::asio::write( socket, boost::asio::buffer(bulletin) ); }
Let's break things down a trivial bit. Here, we are using tcp socket
for communication. read_until
and write
functions from boost::asio
has been used to perform the desired function. boost::asio::buffer
creates a buffer of the information that is being communicated.
Now that we have our functions, let's kick the server in.
int main() { boost::asio::io_service io_service; tcp::acceptor acceptor_(io_service, tcp::endpoint(tcp::v4(), 1234 )); tcp::socket socket_(io_service); acceptor_.take(socket_); string bulletin = read_(socket_); cout < < bulletin < < endl; send_(socket_, " How-do-you-do From Server!"); cout < < " Servent sent Hello message to Customer!" < < endl; return 0; }
io_service
object is needed whenever a program is using asio. tcp::acceptor
is used to listen for connection requested by the client. Nosotros are passing two arguments to the function; ane is the same io_service
object we declared previously and adjacent is the end point of connection beingness initialised to ipv4
and port 1234
. Next server will create a socket and wait for connexion from client. As soon equally the connectedness is built, our read and write operations volition exist executed and connectedness will exist closed.
TCP Client
Nosotros need the other cease for our communication as well, i.east., a customer that requests server. The bones structure is the same equally we did for server.
#include < iostream > #include < boost/asio.hpp > using namespace heave::asio; using ip::tcp; using std::string; using std::cout; using std::endl;
Those are the aforementioned imports as we did for server. Zip new.
int main() { heave::asio::io_service io_service; tcp::socket socket(io_service); socket.connect( tcp::endpoint( heave::asio::ip::address::from_string(" 127.0.0.i"), 1234 )); const string msg = " Hi from Client!\n"; boost::system::error_code error; boost::asio::write( socket, boost::asio::buffer(msg), error ); if( !mistake ) { cout < < " Client sent hello bulletin!" < < endl; } else { cout < < " send failed: " < < error.message() < < endl; } boost::asio::streambuf receive_buffer; boost::asio::read(socket, receive_buffer, heave::asio::transfer_all(), error); if( mistake && error != boost::asio::mistake::eof ) { cout < < " receive failed: " < < mistake.message() < < endl; } else { const char* data = boost::asio::buffer_cast< const char*>(receive_buffer.data()); cout < < data < < endl; } render 0; }
Nosotros again started by creating the io_service
object and creating the socket. We need to connect to server using localhost
(IP 127.0.0.1) and specifying the same port as we did for server to establish the connection successfully. One time the connection is established, we're sending a hullo message to server using boost::asio::write
. If the message is transferred, then server will send back the respond. For this purpose, we have boost::asio::read
function to read dorsum the response. At present let's run our programme to meet things in action.
Compile and run the server by executing the post-obit control:
$ g++ server.cpp -o server –lboost_system $ ./server
Move to the other terminal window to run client.
$ g++ client.cpp -o customer –lboost_system $ ./client
Discover the workflow from the above output. The client sent its asking by proverb howdy to the server after which the server responded back with hello. Once the data transfer is consummate, connection is closed.
TCP Asynchronous Server
The to a higher place programs explain our simple synchronous TCP server and client in which we did the operations in sequential gild, i.e., reading from socket then write. Each operation is blocking which means read operation should cease start and so we can exercise the write operation. Only what if in that location is more than one client trying to connect to server? In instance we don't want our main program to be interrupted while we're reading from or writing to a socket, a multi-threaded TCP client-server is required to handle the situation. The other option is having an asynchronous server. Nosotros can showtime the operation, just we don't know when it volition terminate, we don't demand to know pre-hand either considering it is non blocking. The other operations can be performed adjacent. Think of synchronous equally walkie-talkie where one can speak at a time whereas asynchronous is more similar regular cellphone. At present that we know the basics, let's dive in and attempt to create an asynchronous server.
#include < iostream > #include < boost/asio.hpp > #include < boost/bind.hpp > #include < boost/enable_shared_from_this.hpp > using namespace boost::asio; using ip::tcp; using std::cout; using std::endl;
We have two new imports, demark
and enable_shared_from_this
. We'll exist using the former to bind whatever argument to a specific value and road input arguments into arbitrary positions. The latter is to get a valid shared_ptr
instance.
Allow's define a class to handle the connection as follows:
class con_handler : public heave::enable_shared_from_this<con_handler> { private: tcp::socket sock; std::string message=" Hi From Server!"; enum { max_length = 1024 }; char data[max_length]; public: typedef boost::shared_ptr<con_handler> pointer; con_handler(boost::asio::io_service& io_service): sock(io_service){} static arrow create(boost::asio::io_service& io_service) { return pointer(new con_handler(io_service)); } tcp::socket& socket() { render sock; } void start() { sock.async_read_some( boost::asio::buffer(data, max_length), boost::demark(&con_handler::handle_read, shared_from_this(), boost::asio::placeholders::error, boost::asio::placeholders::bytes_transferred)); sock.async_write_some( boost::asio::buffer(message, max_length), heave::bind(&con_handler::handle_write, shared_from_this(), heave::asio::placeholders::error, boost::asio::placeholders::bytes_transferred)); } void handle_read(const heave::organization::error_code& err, size_t bytes_transferred) { if (!err) { cout < < data < < endl; } else { std::cerr < < " error: " < < err.message() < < std::endl; sock.close(); } } void handle_write(const boost::system::error_code& err, size_t bytes_transferred) { if (!err) { cout < < " Server sent Hello message!" < < endl; } else { std::cerr < < " mistake: " < < err.bulletin() < < endl; sock.close(); } } };
The shared_ptr
and enabled_shared_from_this
is to keep our object alive for whatever operation that refers to it. Then we created the socket pretty much the aforementioned way equally we did in case of synchronous server. Now is the time to specify the functions we want to perform using that socket. We are using async_read_some
and async_write_some
functions to attain the same functionality as that of our previously developed server but now asynchronously. boost::bind
is being used to bind and road the arguments to handle_read/write
. handle_read/write
will now be responsible for any further activity. If yous look into the handle_read
/write
definition, it has the same arguments as the final ii of kicking::bind
and is performing some action in example the data is successfully transferred between client and server or not.
class Server { private: tcp::acceptor acceptor_; void start_accept() { con_handler::arrow connexion = con_handler::create(acceptor_.get_io_service()); acceptor_.async_accept(connection->socket(), boost::demark(&Server::handle_accept, this, connection, boost::asio::placeholders::fault)); } public: Server(boost::asio::io_service& io_service): acceptor_(io_service, tcp::endpoint(tcp::v4(), 1234)) { start_accept(); } void handle_accept(con_handler::arrow connection, const heave::system::error_code& err) { if (!err) { connectedness->first(); } start_accept(); } };
Our server
class will create a socket and will beginning the accept
operation to expect for connection asynchronously. We also defined a constructor for our server
to start listening for connection on the specified port. If no error occurs, connection with the client volition be established. Here's our main function.
int main(int argc, char *argv[]) { try { boost::asio::io_service io_service; Server server(io_service); io_service.run(); } catch(std::exception& e) { std::cerr < < e.what() < < endl; } return 0; }
Over again, nosotros need our io_service
object along with an instance of Server
class. run()
role volition block until all the work is washed, until io_service
is stopped.
It's time to let our monster out. Compile and run the above code with:
$ thousand++ async_server.cpp -o async_server –lboost_system $ ./async_server
Run your client again.
$ ./client
Note that the client airtight the connexion later exchanging the data but server is still up and running. New connections can exist made to the server or else it volition continue to run until explicitly asked to cease. If we want it to finish, so we can practise the post-obit:
boost::optional<boost::asio::io_service::piece of work> work = boost::in_place(heave::ref(io_service)); work = boost::none;
This volition tell run()
that all work is done and non to block anymore.
End Words
This article is not meant to show you the best practices or making you a pro in network programming rather focused to give yous an easy start with socket programming in heave.asio
. It is a pretty handy library and so if you're interested in some loftier-end network programming, I would encourage you to take a deep dive and play around it more. As well, the source lawmaking of both server and client is attached. Experience free to make some changes and let me know if y'all have some good ideas.
C Fdopen for Both Reading and Writing Sockets
Source: https://www.codeproject.com/Articles/1264257/Socket-Programming-in-Cplusplus-using-boost-asio-T