Javascript required
Skip to content Skip to sidebar Skip to footer

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.

Image 1

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.

Image 2

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.

Image 3

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        

Image 4

Image 5

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

Image 6

Image 7

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